From 3e12b75df1b4ba7d77e87fbf92561fa74cb146a1 Mon Sep 17 00:00:00 2001 From: Elmar Wachtmeester Date: Mon, 11 May 2026 14:00:05 +0200 Subject: [PATCH 01/16] Migrate baker-annotations module from Java to Kotlin (#1941) - Migrated all 7 annotation files from Java to Kotlin - Updated pom.xml with Kotlin build configuration - Maintained full backward compatibility with Java consumers - All annotations preserve runtime retention and proper targets - Verified with full Maven build: all 21 modules compile successfully - No breaking changes: existing Java, Scala, and Kotlin code works without modification --- core/baker-annotations/pom.xml | 50 +++++++++++++------ .../recipe/annotations/AsyncInteraction.java | 15 ------ .../baker/recipe/annotations/FiresEvent.java | 15 ------ .../baker/recipe/annotations/ProcessId.java | 15 ------ .../annotations/RecipeInstanceEventList.java | 15 ------ .../recipe/annotations/RecipeInstanceId.java | 15 ------ .../annotations/RecipeInstanceMetadata.java | 15 ------ .../annotations/RequiresIngredient.java | 15 ------ .../recipe/annotations/AsyncInteraction.kt | 12 +++++ .../baker/recipe/annotations/FiresEvent.kt | 15 ++++++ .../ing/baker/recipe/annotations/ProcessId.kt | 13 +++++ .../annotations/RecipeInstanceEventList.kt | 13 +++++ .../recipe/annotations/RecipeInstanceId.kt | 13 +++++ .../annotations/RecipeInstanceMetadata.kt | 13 +++++ .../recipe/annotations/RequiresIngredient.kt | 14 ++++++ .../baker/recipe/annotations/package.scala | 8 --- 16 files changed, 129 insertions(+), 127 deletions(-) delete mode 100644 core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/AsyncInteraction.java delete mode 100644 core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/FiresEvent.java delete mode 100644 core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/ProcessId.java delete mode 100644 core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceEventList.java delete mode 100644 core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceId.java delete mode 100644 core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.java delete mode 100644 core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RequiresIngredient.java create mode 100644 core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/AsyncInteraction.kt create mode 100644 core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/FiresEvent.kt create mode 100644 core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/ProcessId.kt create mode 100644 core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceEventList.kt create mode 100644 core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceId.kt create mode 100644 core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.kt create mode 100644 core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RequiresIngredient.kt delete mode 100644 core/baker-annotations/src/main/scala/com/ing/baker/recipe/annotations/package.scala diff --git a/core/baker-annotations/pom.xml b/core/baker-annotations/pom.xml index a5423910a..66eddbe8c 100644 --- a/core/baker-annotations/pom.xml +++ b/core/baker-annotations/pom.xml @@ -16,34 +16,56 @@ Annotations for Baker framework - - org.scala-lang - scala-library - - javax.inject javax.inject - - - org.scalatest - scalatest_${scala.compat.version} - test + org.jetbrains.kotlin + kotlin-stdlib + - net.alchim31.maven - scala-maven-plugin + org.codehaus.mojo + build-helper-maven-plugin + ${build.helper.maven.plugin.version} + + + add-source + generate-sources + + add-source + + + + src/main/kotlin + + + + + - org.scalatest - scalatest-maven-plugin + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + ${jvm.target} + + + + compile + process-sources + + compile + + + diff --git a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/AsyncInteraction.java b/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/AsyncInteraction.java deleted file mode 100644 index 9c3d29292..000000000 --- a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/AsyncInteraction.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ing.baker.recipe.annotations; - -import javax.inject.Qualifier; -import java.lang.annotation.*; - -/** - * Annotation for an interaction to show it supports Async calls - */ -@Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface AsyncInteraction { - -} diff --git a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/FiresEvent.java b/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/FiresEvent.java deleted file mode 100644 index b57422b7c..000000000 --- a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/FiresEvent.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ing.baker.recipe.annotations; - -import javax.inject.Qualifier; -import java.lang.annotation.*; - -/** - * Annotation for an interaction to show what events can fired from this interaction - */ -@Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface FiresEvent { - Class[] oneOf(); -} diff --git a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/ProcessId.java b/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/ProcessId.java deleted file mode 100644 index 4234b4510..000000000 --- a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/ProcessId.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ing.baker.recipe.annotations; - -import javax.inject.Qualifier; -import java.lang.annotation.*; - -/** - * An annotation to be added to an argument of an action indicating that the process identifier should be injected - * there. - */ -@Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface ProcessId { -} diff --git a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceEventList.java b/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceEventList.java deleted file mode 100644 index 2e011537b..000000000 --- a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceEventList.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ing.baker.recipe.annotations; - -import javax.inject.Qualifier; -import java.lang.annotation.*; - -/** - * An annotation to be added to an argument of an action indicating that the Event List should be injected - * there. - */ -@Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface RecipeInstanceEventList { -} diff --git a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceId.java b/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceId.java deleted file mode 100644 index bcc8d5d62..000000000 --- a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceId.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ing.baker.recipe.annotations; - -import javax.inject.Qualifier; -import java.lang.annotation.*; - -/** - * An annotation to be added to an argument of an action indicating that the process identifier should be injected - * there. - */ -@Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface RecipeInstanceId { -} diff --git a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.java b/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.java deleted file mode 100644 index 90fb10320..000000000 --- a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ing.baker.recipe.annotations; - -import javax.inject.Qualifier; -import java.lang.annotation.*; - -/** - * An annotation to be added to an argument of an action indicating that the Metadata should be injected - * there. - */ -@Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface RecipeInstanceMetadata { -} diff --git a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RequiresIngredient.java b/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RequiresIngredient.java deleted file mode 100644 index 4e87c84e1..000000000 --- a/core/baker-annotations/src/main/java/com/ing/baker/recipe/annotations/RequiresIngredient.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ing.baker.recipe.annotations; - -import javax.inject.Qualifier; -import java.lang.annotation.*; - -/** - * An annotation to be added to an argument of an interaction to show what ingredient should be used here. - */ -@Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface RequiresIngredient { - String value() default ""; -} diff --git a/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/AsyncInteraction.kt b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/AsyncInteraction.kt new file mode 100644 index 000000000..e3df24f05 --- /dev/null +++ b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/AsyncInteraction.kt @@ -0,0 +1,12 @@ +package com.ing.baker.recipe.annotations + +import javax.inject.Qualifier + +/** + * Annotation for an interaction to show it supports Async calls + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class AsyncInteraction diff --git a/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/FiresEvent.kt b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/FiresEvent.kt new file mode 100644 index 000000000..30ac1e34c --- /dev/null +++ b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/FiresEvent.kt @@ -0,0 +1,15 @@ +package com.ing.baker.recipe.annotations + +import javax.inject.Qualifier +import kotlin.reflect.KClass + +/** + * Annotation for an interaction to show what events can fired from this interaction + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class FiresEvent( + val oneOf: Array> +) diff --git a/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/ProcessId.kt b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/ProcessId.kt new file mode 100644 index 000000000..57d2061af --- /dev/null +++ b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/ProcessId.kt @@ -0,0 +1,13 @@ +package com.ing.baker.recipe.annotations + +import javax.inject.Qualifier + +/** + * An annotation to be added to an argument of an action indicating that the process identifier should be injected + * there. + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class ProcessId diff --git a/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceEventList.kt b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceEventList.kt new file mode 100644 index 000000000..025201cbd --- /dev/null +++ b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceEventList.kt @@ -0,0 +1,13 @@ +package com.ing.baker.recipe.annotations + +import javax.inject.Qualifier + +/** + * An annotation to be added to an argument of an action indicating that the Event List should be injected + * there. + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class RecipeInstanceEventList diff --git a/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceId.kt b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceId.kt new file mode 100644 index 000000000..109f74eca --- /dev/null +++ b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceId.kt @@ -0,0 +1,13 @@ +package com.ing.baker.recipe.annotations + +import javax.inject.Qualifier + +/** + * An annotation to be added to an argument of an action indicating that the process identifier should be injected + * there. + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class RecipeInstanceId diff --git a/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.kt b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.kt new file mode 100644 index 000000000..a5acc7910 --- /dev/null +++ b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RecipeInstanceMetadata.kt @@ -0,0 +1,13 @@ +package com.ing.baker.recipe.annotations + +import javax.inject.Qualifier + +/** + * An annotation to be added to an argument of an action indicating that the Metadata should be injected + * there. + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class RecipeInstanceMetadata diff --git a/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RequiresIngredient.kt b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RequiresIngredient.kt new file mode 100644 index 000000000..eb716e493 --- /dev/null +++ b/core/baker-annotations/src/main/kotlin/com/ing/baker/recipe/annotations/RequiresIngredient.kt @@ -0,0 +1,14 @@ +package com.ing.baker.recipe.annotations + +import javax.inject.Qualifier + +/** + * An annotation to be added to an argument of an interaction to show what ingredient should be used here. + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.VALUE_PARAMETER) +annotation class RequiresIngredient( + val value: String = "" +) diff --git a/core/baker-annotations/src/main/scala/com/ing/baker/recipe/annotations/package.scala b/core/baker-annotations/src/main/scala/com/ing/baker/recipe/annotations/package.scala deleted file mode 100644 index 8971c34f6..000000000 --- a/core/baker-annotations/src/main/scala/com/ing/baker/recipe/annotations/package.scala +++ /dev/null @@ -1,8 +0,0 @@ -package com.ing.baker.recipe - -/** - * Added this object ot fix our build - */ -package object annotations { - -} From 79d6d5b7ca1117436ef4fb8a8a81b74e53bd30bc Mon Sep 17 00:00:00 2001 From: Elmar Wachtmeester Date: Tue, 24 Feb 2026 10:41:19 +0100 Subject: [PATCH 02/16] Initial version of Kotlin Recipe compiler --- .../src/test/kotlin/RecipeCompilerSpecTest.kt | 644 +++++++++++++++++ .../baker/compiler/RecipeCompilerSpec.scala | 14 +- .../baker/recipe/kotlindsl/KotlinDslTest.kt | 2 +- .../recipe/scaladsl/CheckPointEvent.scala | 4 + .../ing/baker/compiler/AssertionsKotlin.kt | 51 ++ .../baker/compiler/RecipeCompilerKotlin.kt | 680 ++++++++++++++++++ .../ing/baker/compiler/ScalaConversions.kt | 20 + 7 files changed, 1409 insertions(+), 6 deletions(-) create mode 100644 core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt create mode 100644 core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt create mode 100644 core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt create mode 100644 core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt diff --git a/core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt b/core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt new file mode 100644 index 000000000..081474cbb --- /dev/null +++ b/core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt @@ -0,0 +1,644 @@ +package com.ing.baker.compiler + +import com.ing.baker.il.* +import com.ing.baker.il.RecipeVisualStyle.* +import com.ing.baker.il.petrinet.InteractionTransition +import com.ing.baker.recipe.TestRecipe.getRecipe +import com.ing.baker.recipe.TestRecipe.* +import com.ing.baker.recipe.TestRecipeJava +import com.ing.baker.recipe.common.EventOutputTransformer +import com.ing.baker.recipe.common.Ingredient +import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.scaladsl.* +import com.ing.baker.types.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.jupiter.api.fail +import scala.* +import scala.collection.immutable.Seq +import scala.concurrent.duration.Duration +import java.util.* +import java.util.concurrent.TimeUnit +import kotlin.reflect.javaType +import kotlin.reflect.typeOf +import com.ing.baker.compiler.ScalaConversions.asScala +import com.ing.baker.compiler.ScalaConversions.asJava + + +class RecipeCompilerScalaTest { + @Test + fun `The recipe compiler should not have validation errors for a valid recipe`() { + val recipe: Recipe = getRecipe("ValidRecipe") + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals(emptyList(), compiledRecipe.validationErrors) + + // dumpToFile("TestRecipe.svg", compiledRecipe.getVisualRecipeAsSVG) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined`() { + val exhaustedEvent = event("RetryExhausted", emptyList()) + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireRetryExhaustedEvent(exhaustedEvent) + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(exhaustedEvent.name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should generate the same id for the same recipe`() { + val first = RecipeCompiler.compileRecipe(getRecipe("ValidRecipe")).recipeId() + (1.. 10) + .map { getRecipe("ValidRecipe")} + .map{ RecipeCompiler.compileRecipe(it).recipeId() } + .forEach { + assertEquals(first, it) + } + } + + @Test + fun `The recipe compiler should generate different ids for recipes with changes on transitions other than the name`() { + val input = ingredient("ingredient") + val output = event("event", emptyList()) + val interaction = interaction("interaction", listOf(input), listOf(output)) + val name = "RecipeName" + val recipe1 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 1)) + val recipe2 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 2)) + assertTrue(RecipeCompiler.compileRecipe(recipe1).recipeId() != RecipeCompiler.compileRecipe(recipe2).recipeId()) + } + + @Test + fun `The recipe compiler should give a list of missing ingredients if an interaction has an ingredient that is not provided by any other event or interaction`() { + val recipe = recipe("NonProvidedIngredient") + .withSensoryEvent(secondEvent) + .withInteractions(interactionOne) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give an error if the RecipeInstanceId is required and is not of the String type`() { + val wrongrecipeInstanceIdInteraction = + interaction( + name = "wrongrecipeInstanceIdInteraction", + inputIngredients = listOf(ingredient(common.recipeInstanceIdName), initialIngredient), + output = emptyList() + ) + + val recipe = recipe("NonProvidedIngredient") + .withSensoryEvent(initialEvent) + .withInteractions(wrongrecipeInstanceIdInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Non supported process id type: Int32 on interaction: 'wrongrecipeInstanceIdInteraction'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give an error if the MetaData is required and is not of the Map String to String type`() { + val wrongMetaDataInteraction = + interaction( + name = "wrongMetaDataInteraction", + inputIngredients = listOf(ingredient(common.recipeInstanceMetadataName), initialIngredient), + output = emptyList() + ) + + val recipe = recipe("NonProvidedIngredient") + .withSensoryEvent(initialEvent) + .withInteractions(wrongMetaDataInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Non supported MetaData type: Int32 on interaction: 'wrongMetaDataInteraction'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give an error if the baker internal ingredients are provided`() { + val wrongDateEvent = event("WrongDataEvent", + listOf( + ingredient("recipeInstanceId"), + ingredient("RecipeInstanceMetaData")), + maxFiringLimit = null) + + val wrongDateEvent2 = event("WrongDataEvent2", + listOf( + ingredient("RecipeInstanceEventList")), + maxFiringLimit = null) + + val wrongMetaDataInteraction = + interaction( + name = "wrongDataProvidedInteraction", + inputIngredients = listOf(ingredient(common.recipeInstanceIdName), initialIngredient), + output = listOf(wrongDateEvent2) + ) + + val recipe = recipe("WrongDataRecipe") + .withSensoryEvents(initialEvent, wrongDateEvent) + .withInteractions(wrongMetaDataInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals(listOf( + "Ingredient 'RecipeInstanceMetaData' is provided and this is a reserved name for internal use in Baker", + "Ingredient 'recipeInstanceId' is provided and this is a reserved name for internal use in Baker", + "Ingredient 'RecipeInstanceEventList' is provided and this is a reserved name for internal use in Baker"), + compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients when an ingredient is of the wrong type`() { + val initialIngredientInt = Ingredient("initialIngredient", recordType(listOf( + RecordField("data", Int32) + ))) + val initialEventInt = event("InitialEvent", listOf(initialIngredientInt), null) + + val recipe = recipe("WrongTypedIngredient") + .withInteractions( + interactionOne) + .withSensoryEvent(initialEventInt) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Interaction 'InteractionOne' expects ingredient 'initialIngredient:CharArray', however incompatible type: 'Record(data: Int32)' was provided" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients when an optional ingredient is of the wrong Option type`() { + val initialIngredientOptionalInt = ingredient>("initialIngredientOptionalInt") + val initialIngredientOptionalString = ingredient>("initialIngredientOptionalInt") + val initialIngredientOptionInt = ingredient>>("initialIngredientOptionInt") + val initialIngredientOptionString = ingredient>>("initialIngredientOptionInt") + val initialEventIntOptional = event("initialEventIntOptional", listOf(initialIngredientOptionalString), null) + val initialEventIntOption = event("initialEventIntOption", listOf(initialIngredientOptionString), null) + val interactionOptional = + interaction( + name = "InteractionWithOptional", + inputIngredients = listOf(recipeInstanceId, initialIngredientOptionalInt, initialIngredientOptionInt), + output = emptyList() + ) + + val recipe = recipe("WrongTypedOptionalIngredient") + .withInteractions( + interactionOptional) + .withSensoryEvents(initialEventIntOptional, initialEventIntOption) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionalInt:Option[Int32]', however incompatible type: 'Option[CharArray]' was provided" in compiledRecipe.validationErrors) + assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionInt:Option[List[Int32]]', however incompatible type: 'Option[List[CharArray]]' was provided" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give no errors if an Optional ingredient is of the correct Option type`(){ + val initialIngredientInt = ingredient>>("initialIngredient") + val initialEventInt = event("InitialEvent", listOf(initialIngredientInt), null) + val interactionOptional = + interaction( + name = "InteractionWithOptional", + inputIngredients = listOf(recipeInstanceId, initialIngredientInt), + output = emptyList() + ) + + val recipe = recipe("CorrectTypedOptionalIngredient") + .withInteractions( + interactionOptional) + .withSensoryEvent(initialEventInt) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(compiledRecipe.validationErrors.isEmpty()) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients if a predefined ingredient is of the wrong type`() { + val recipe = recipe("WrongGivenPredefinedIngredient") + .withInteractions( + interactionOne + .withRequiredEvent(initialEvent) + .withPredefinedIngredients("initialIngredient" to Integer.valueOf(12))) + .withSensoryEvent(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Predefined argument 'initialIngredient' is not of type: CharArray on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients if a predefined ingredient is not needed by the interaction`() { + val recipe = recipe("WrongGivenIngredient") + .withInteractions( + interactionOne + .withPredefinedIngredients("WrongIngredient" to null)) + .withSensoryEvent(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Predefined argument 'WrongIngredient' is not defined on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should validate if unreachable interactions exist or not`() { + val recipe = recipe("RecipeWithUnreachableInteraction") + .withInteractions(interactionSeven.withMaximumInteractionCount(1), interactionEight) + .withSensoryEvent(initialEvent) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe, + validationSettings(allowNonExecutableInteractions = false) + ) + + assertTrue("InteractionEight is not executable" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should fail compilation when the interaction name is null or empty`() { + listOf("", null).forEach { name -> + val invalidInteraction = interaction(name, emptyList(), emptyList()) + val recipe = recipe("InteractionNameTest").withInteractions(invalidInteraction).withSensoryEvent(initialEvent) + + assertFailsWith(exceptionMessage = "Interaction with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + fun `The recipe compiler should fail compilation when the event name is null or empty`() { + listOf("", null).forEach { name -> + val invalidEvent = event(name, emptyList()) + val recipe = recipe("EventNameTest").withSensoryEvent(invalidEvent).withInteraction(interactionOne) + + assertFailsWith(exceptionMessage = "Event with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + fun `The recipe compiler should fail compilation when the ingredient name is null or empty`() { + listOf("", null).forEach { name -> + val invalidIngredient = ingredient(name) + val recipe = recipe("IngredientNameTest").withSensoryEvent(event("someEvent", listOf(invalidIngredient))).withInteraction(interactionOne) + + assertFailsWith(exceptionMessage = "Ingredient with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + fun `The recipe compiler should fail compilation when the recipe name is null or empty`() { + listOf("", null).forEach { name -> + val recipe = recipe(name) + + assertFailsWith(exceptionMessage = "Recipe with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + //TODO Change name of the test. compiler is not failing + fun `The recipe compiler should fail compilation when an Interaction is reprovider, but has no required events`() { + val recipe: Recipe = recipe("LoopingWithReprovider") + .withInteraction(interactionOne.isReprovider(true)) + .withSensoryEvents(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals(listOf("Reprovider interaction InteractionOne needs to have a event precondition"), compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient as empty the ingredient is not provided`() { + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients) + .withSensoryEvent(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(4, transition.predefinedParameters().size()) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is provided`() { + val optionalProviderEvent = event("optionalProviderEvent", listOf(missingJavaOptional)) + + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients) + .withSensoryEvents(initialEvent, optionalProviderEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(3, transition.predefinedParameters().size()) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is renamed and provided via an event`() { + val stringOptionIngredient = ingredient>("stringOptionIngredient") + val renamedStringOptionIngredient = ingredient>("renamedStringOptionIngredient") + + val eventWithOptionIngredient = event("eventWithOptionIngredient", listOf(stringOptionIngredient)) + + val interactionWithOptionIngredient = interaction("interactionWithOptionIngredient", listOf(initialIngredient), listOf(eventWithOptionIngredient)) + + val secondInteraction = interaction("secondInteraction", listOf(renamedStringOptionIngredient), emptyList()) + + val recipe = recipe("interactionWithEventOutputTransformer") + .withSensoryEvent(initialEvent) + .withInteraction(interactionWithOptionIngredient + .withEventOutputTransformer(eventWithOptionIngredient, "RenamedEventWithOptionIngredient", mapOf("stringOptionIngredient" to "renamedStringOptionIngredient").asScala)) + .withInteraction(secondInteraction) + + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(compiledRecipe.validationErrors.isEmpty()) + + val transition = compiledRecipe.interactionTransitions().find{ it.interactionName() == "secondInteraction" }.get() + assertTrue("renamedStringOptionIngredient" in transition.nonProvidedIngredients().asJava.map(IngredientDescriptor::name)) + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is predefined`() { + val ingredientValue = Optional.of("value") + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction( + interactionWithOptionalIngredients + .withPredefinedIngredients("missingJavaOptional" to ingredientValue) + ) + .withSensoryEvents(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(4, transition.predefinedParameters().size()) + assertEquals(Some(PrimitiveValue("value")), transition.predefinedParameters()["missingJavaOptional"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is provided, but not wrapped in an Option type`() { + val optionalProviderEvent = event("optionalProviderEvent", listOf(missingJavaOptionalDirectString)) + + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients) + .withSensoryEvents(initialEvent, optionalProviderEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(3, transition.predefinedParameters().size()) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the correct id when it compiles a java recipe`() { + val recipe = TestRecipeJava.getRecipe("id-test-recipe") + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals("220827c42a75b3f8", compiledRecipe.recipeId()) + } + + @Test + fun `The recipe compiler should give the interaction with Reprovider enabled when it compiles a java recipe and changes recipeId`() { + val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals("416e8abc02abcbee", compiledRecipe.recipeId()) + } + + @Test + fun `The recipe compiler should give the interaction for checkpoint-events when it compiles a java recipe`() { + val recipe = TestRecipeJava.getRecipe("id-test-recipe") + .withCheckpointEvent( + checkPointEvent("Success") + .withRequiredEvent(initialEvent) + ) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals("469441173f91869a", compiledRecipe.recipeId()) + assertEquals(1, compiledRecipe.petriNet().transitions().count { it is InteractionTransition && it.interactionName() + .contains("${checkpointEventInteractionPrefix}Success") }) + } + + @Test + fun `give the interaction for sub-recipes when it compiles a java recipe`() { + + val subSubRecipe: Recipe = recipe("SubSubRecipe") + .withInteractions( + interactionOne + .withEventOutputTransformer(interactionOneSuccessful, mapOf("interactionOneOriginalIngredient" to "interactionOneIngredient").asScala) + .withFailureStrategy(InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Option.apply(UntilMaximumRetries(3))) + .build()), + interactionTwo + .withOverriddenIngredientName("initialIngredientOld", "initialIngredient"), + ) + + val subRecipe: Recipe = recipe("SubRecipe") + .withInteractions( + interactionThree + .withMaximumInteractionCount(1), + interactionFour + .withRequiredEvents(secondEvent, eventFromInteractionTwo), + interactionFive, + interactionSix, + ) + .withSubRecipe(subSubRecipe) + + val recipe: Recipe = recipe("Recipe") + .withSensoryEvents( + initialEvent, + initialEventExtendedName, + secondEvent, + notUsedSensoryEvent + ) + .withInteractions( + providesNothingInteraction, + sieveInteraction + ) + .withSubRecipe(subRecipe) + + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + val res = compiledRecipe.petriNet().transitions().asJava + .flatMap { when(it) { + is InteractionTransition -> listOf(it.interactionName()) + else -> emptyList() + }} + .filter{ it.startsWith(subRecipePrefix) }.toSet() + + assertEquals("ae2282f55f0a4f9f", compiledRecipe.recipeId()) + assertEquals(setOf( + "\$SubRecipe\$SubSubRecipe\$InteractionOne", + "\$SubRecipe\$SubSubRecipe\$InteractionTwo", + "\$SubRecipe\$SubRecipe\$InteractionThree", + "\$SubRecipe\$SubRecipe\$InteractionFour", + "\$SubRecipe\$SubRecipe\$InteractionFive", + "\$SubRecipe\$SubRecipe\$InteractionSix", + ), res) + + val vis = RecipeVisualizer.visualizeRecipe(compiledRecipe, `RecipeVisualStyle$`.`MODULE$`.default(), emptySet().asScala, emptySet().asScala, emptySet().asScala) + + println(vis) + } + + + + + private inline fun assertFailsWith(exceptionMessage: String, message: String? = null, init: () -> Any?) { + try { + init() + } catch (t: Throwable) { + if (t is T) { + if (t.message == exceptionMessage) + return + else + fail(message ?: "Exception ${T::class.simpleName} thrown but message is different") + } + fail(message ?: "Exception thrown but not ${T::class.simpleName}") + } + fail(message ?: "No exception thrown") + } + + + private fun validationSettings(allowCycles: Boolean = true, allowDisconnectedness: Boolean = true, allowNonExecutableInteractions: Boolean = true) = + ValidationSettings(allowCycles, allowDisconnectedness, allowNonExecutableInteractions) + + + private fun recordType(recordsFields: List) = RecordType(recordsFields.asScala) + + + private fun recipe(name: String?): Recipe = Recipe.apply(name) + private fun event(name: String?, ingredients: List, maxFiringLimit: Int? = null) = Event.apply(name, ingredients.asScala).let { + when(maxFiringLimit) { + null -> it + else -> it.withMaxFiringLimit(maxFiringLimit) + } + } + + + + // Scala glue + val initialEvent = initialEvent() + val secondEvent = secondEvent() + val interactionOneSuccessful = interactionOneSuccessful() + val eventFromInteractionTwo = eventFromInteractionTwo() + val initialEventExtendedName = initialEventExtendedName() + val notUsedSensoryEvent = notUsedSensoryEvent() + + val interactionOne = interactionOne() + val interactionTwo = interactionTwo() + val interactionThree = interactionThree() + val interactionFour = interactionFour() + val interactionFive = interactionFive() + val interactionSix = interactionSix() + val interactionSeven = interactionSeven() + val interactionEight = interactionEight() + val interactionWithOptionalIngredients = interactionWithOptionalIngredients() + val providesNothingInteraction = providesNothingInteraction() + val sieveInteraction = sieveInteraction() + + val initialIngredient = initialIngredient() + val missingJavaOptional = missingJavaOptional() + val missingJavaOptionalDirectString = missingJavaOptionalDirectString() + + val recipeInstanceId = ingredient(common.recipeInstanceIdName) + + fun Recipe.withInteractions(vararg newInteractions: InteractionDescriptor) = this.withInteractions(newInteractions.asScala as Seq?) + fun Recipe.withSensoryEvents(vararg sensoryEvent: com.ing.baker.recipe.common.Event) = this.withSensoryEvents(sensoryEvent.asScala as Seq) + val Int.milliseconds get() = Duration.create(this.toLong(), TimeUnit.MILLISECONDS) + val Int.seconds get() = Duration.create(this.toLong(), TimeUnit.SECONDS) + + + object common { + val recipeInstanceIdName = com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() + val recipeInstanceMetadataName = com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() + } + + @OptIn(ExperimentalStdlibApi::class) + inline fun ingredient(name: String?) = Ingredient(name, Converters.readJavaType(typeOf().javaType)) + + private fun checkPointEvent(name: String): CheckPointEvent = + `CheckPointEvent$`.`MODULE$`.apply(name) + + private fun interaction( + name: String?, + inputIngredients: List, + output: List, + requiredEvents: Set = emptySet(), + requiredOneOfEvents: Set> = emptySet(), + predefinedIngredients: Map = emptyMap(), + overriddenIngredientNames: Map = emptyMap(), + overriddenOutputIngredientName: String? = null, + maximumInteractionCount: Int? = null, + failureStrategy: com.ing.baker.recipe.common.InteractionFailureStrategy? = null, + eventOutputTransformers: Map = emptyMap(), + isReprovider: Boolean = false, + oldName: String? = null, + ): Interaction = + `Interaction$`.`MODULE$`.apply( + name, + inputIngredients.asScala, + output.asScala, + requiredEvents.asScala, + requiredOneOfEvents.map { it: Set -> it.asScala}.toSet().asScala, + predefinedIngredients.asScala, + overriddenIngredientNames.asScala, + Option.apply(overriddenOutputIngredientName), + Option.apply(maximumInteractionCount), + Option.apply(failureStrategy), + eventOutputTransformers.asScala, + isReprovider, + Option.apply(oldName) + ) + + // Ugly Scala glue + val Int32 = `Int32$`.`MODULE$` + val NullValue = `NullValue$`.`MODULE$` + val checkpointEventInteractionPrefix = com.ing.baker.il.`package$`.`MODULE$`.checkpointEventInteractionPrefix() + val subRecipePrefix = com.ing.baker.il.`package$`.`MODULE$`.subRecipePrefix() + + fun Interaction.withPredefinedIngredients(vararg ingredients: Pair) = this.withPredefinedIngredients(ingredients.map { Tuple2(it.first, it.second) }.asScala) + fun Interaction.withRequiredEvents(vararg events: com.ing.baker.recipe.common.Event) = this.withRequiredEvents(events.toList().asScala) + + object InteractionFailureStrategy { + object RetryWithIncrementalBackoff { + fun builder() = com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$builder$`.`MODULE$`.apply() + } + } + + object UntilDeadline { + operator fun invoke(duration: Duration) = com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$UntilDeadline$`.`MODULE$`.apply(duration) + + } + object UntilMaximumRetries { + operator fun invoke(maxRetries: Int) = com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$UntilMaximumRetries$`.`MODULE$`.apply(maxRetries) + } + +} \ No newline at end of file diff --git a/core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala b/core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala index 46263a9ec..ff157948d 100644 --- a/core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala +++ b/core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala @@ -618,11 +618,15 @@ class RecipeCompilerSpec extends AnyWordSpecLike with Matchers { } .filter( _.startsWith(subRecipePrefix)) -// compiledRecipe.recipeId shouldBe "fb5346c571ca5a47" -// res shouldBe Set( -// "$SubRecipe$SubRecipe$InteractionSeven", -// "$SubRecipe$SubSubRecipe$InteractionOne" -// ) + compiledRecipe.recipeId shouldBe "ae2282f55f0a4f9f" + res shouldBe Set( + "$SubRecipe$SubSubRecipe$InteractionOne", + "$SubRecipe$SubSubRecipe$InteractionTwo", + "$SubRecipe$SubRecipe$InteractionThree", + "$SubRecipe$SubRecipe$InteractionFour", + "$SubRecipe$SubRecipe$InteractionFive", + "$SubRecipe$SubRecipe$InteractionSix", + ) val vis = RecipeVisualizer.visualizeRecipe(compiledRecipe, RecipeVisualStyle.default) diff --git a/core/baker-recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt b/core/baker-recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt index 6851c1804..8f1d11982 100644 --- a/core/baker-recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt +++ b/core/baker-recipe-dsl-kotlin/src/test/kotlin/com/ing/baker/recipe/kotlindsl/KotlinDslTest.kt @@ -37,7 +37,7 @@ class KotlinDslTest { } // Ingredient definition failed with optionals - ingredient("test") @JvmSerializableLambda { test: Optional -> "" } + ingredient("test", @JvmSerializableLambda { test: Optional -> "" }) sensoryEvents { eventWithoutFiringLimit() diff --git a/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala b/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala index 64dbb525c..c9be0f133 100644 --- a/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala +++ b/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala @@ -2,6 +2,10 @@ package com.ing.baker.recipe.scaladsl import com.ing.baker.recipe.common +object CheckPointEvent { + def apply(name: String): CheckPointEvent = CheckPointEvent(name, Set.empty, Set.empty) +} + case class CheckPointEvent private( override val name: String = "", override val requiredEvents: Set[String] = Set.empty, diff --git a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt new file mode 100644 index 000000000..b7090a01b --- /dev/null +++ b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt @@ -0,0 +1,51 @@ +package com.ing.baker.compiler + +import com.ing.baker.recipe.common.Event +import com.ing.baker.recipe.common.Ingredient +import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.common.Recipe +import scala.Some + +private fun assertNoDuplicateElementsExist(compareIdentifier: (T) -> Any, elements: Iterable): Unit = + when (val duplicates = elements.groupBy { compareIdentifier(it) }.values.firstOrNull { it.size > 1 }) { + null -> Unit + else -> { + val e = duplicates[0] + val c = duplicates[1] + throw IllegalStateException("Duplicate identifiers found: ${e::class.java.getSimpleName()}:$e and ${c::class.java.getSimpleName()}:$c") + } + } + +private fun assertValidNames(nameFunc: (T) -> String?, list: Iterable, typeName: String): Unit = + if (list.map(nameFunc).any { name -> (name == null) || (name.isEmpty())}) + throw IllegalArgumentException("$typeName with a null or empty name found") + else + Unit + +private fun assertNonEmptyRecipe(recipe: Recipe): List = + listOf( + if (recipe.sensoryEvents.isEmpty()) "No sensory events found." else null, + if (recipe.interactions.isEmpty()) "No interactions found." else null, + ).filterNotNull() + +private fun assertRequiredEventForReprovider(recipe: Recipe): List = + recipe.interactions.filter { interaction -> + interaction.isReprovider && interaction.requiredEvents.isEmpty() && interaction.requiredOneOfEvents.isEmpty() + }.map{interaction -> "Reprovider interaction ${interaction.name()} needs to have a event precondition"} + + +private fun assertSensoryEventsNegativeFiringLimits(recipe: Recipe): List = + listOf( + if (recipe.sensoryEvents.map(Event::maxFiringLimit).filterIsInstance(Some::class.java).any { it.value() as Int <= 0 }) "MaxFiringLimit should be greater than 0" else null + ).filterNotNull() + +fun preCompileAssertions(recipe: Recipe): List { + assertValidNames(Recipe::name, listOf(recipe), "Recipe") + assertValidNames(InteractionDescriptor::name, recipe.interactions, "Interaction") + assertValidNames(Event::name, recipe.sensoryEvents, "Event") + val allIngredients = recipe.sensoryEvents.flatMap(Event::providedIngredients) + recipe.interactions.flatMap(InteractionDescriptor::inputIngredients) + assertValidNames(Ingredient::name, allIngredients, "Ingredient") + assertNoDuplicateElementsExist(InteractionDescriptor::name, recipe.interactions) + assertNoDuplicateElementsExist(Event::name, recipe.sensoryEvents) + return assertSensoryEventsNegativeFiringLimits(recipe) + assertRequiredEventForReprovider(recipe) +} \ No newline at end of file diff --git a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt new file mode 100644 index 000000000..7fd879967 --- /dev/null +++ b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt @@ -0,0 +1,680 @@ +package com.ing.baker.compiler + +import com.ing.baker.compiler.ScalaConversions.asJava +import com.ing.baker.compiler.ScalaConversions.asScala +import com.ing.baker.il.* +import com.ing.baker.il.CompiledRecipe.OldRecipeIdVariant +import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff +import com.ing.baker.il.petrinet.Edge +import com.ing.baker.il.petrinet.MissingEventTransition +import com.ing.baker.il.petrinet.Place +import com.ing.baker.il.petrinet.Transition +import scala.Option +import scala.util.Left +import scala.util.Right + +import com.ing.baker.il.petrinet.* +import com.ing.baker.il.petrinet.Place.FiringLimiterPlace +import com.ing.baker.petrinet.api.* +import com.ing.baker.recipe.common.* +import com.ing.baker.recipe.common.EventOutputTransformer +import com.ing.baker.recipe.scaladsl.`Event$` +import com.ing.baker.types.* +import scala.Some +import scala.collection.immutable.Seq +import scala.reflect.ClassTag +import scala.util.Either +import scalax.collection.GraphEdge +import scalax.collection.config.CoreConfig +import scalax.collection.config.GraphConfig +import scalax.collection.edge.WLDiEdge +import scalax.collection.edge.`WLDiEdge$` +import scalax.collection.`Graph$` +import scalax.collection.Graph +import scalax.collection.mutable.ArraySet.Hints + +/** + * Type alias for a petri net with recipe Place and Transition types + */ +typealias RecipePetriNet = PetriNet + +/** + * Type alias for the node type of the scalax.collection.Graph backing the petri net. + */ +typealias Node = Either + +/** + * Type alias for the edge type of the scalax.collection.Graph backing the petri net. + */ +typealias Arc = WLDiEdge + +/** + * Type alias for a multi set. + */ +typealias MultiSet = Map + +/** + * Type alias for a marking. + */ +typealias Marking = Map> + + + +object RecipeCompilerKotlin { + fun List, List>>.unzipFlatten() = this.unzip().let { pair -> + pair.first.flatten() to pair.second.flatten() } + + + fun transition(transition: Transition) = Right(transition) + fun place(place: Place) = Left(place) + + fun arc(t: Transition, p: Place, weight: Long): Arc = + wlDiEdge(transition(t), place(p), weight.toDouble(), Edge(Option.empty())) + + + fun arc(p: Place, t: Transition, weight: Long, eventFilter: String? = null): Arc = + wlDiEdge(place(p), transition(t), weight.toDouble(), Edge(Option.apply(eventFilter))) + + /** + * Creates a transition for a missing event in the recipe. + */ + private fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) + + private fun buildEventAndPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition) = + + //Find the event in available events + interaction.requiredEvents.map { eventName -> + // a new `Place` generated for each AND events + val eventPreconditionPlace = + createPlace( + label = "$eventName-${interaction.name()}", + placeType = Place.`EventPreconditionPlace$`.`MODULE$` + ) + + buildEventPreconditionArcs( + eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name()) + ) + }.unzipFlatten() + + + private fun buildEventORPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition) = + + interaction.requiredOneOfEvents.mapIndexed { index: Int, orGroup: Set -> + // only one `Place` for all the OR events + val eventPreconditionPlace = createPlace( + label = "${interaction.name()}-or-$index", + placeType = Place.`EventPreconditionPlace$`.`MODULE$` + ) + + orGroup.map { eventName -> + buildEventPreconditionArcs(eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name())) + }.unzipFlatten() + }.unzipFlatten() + + private fun buildEventPreconditionArcs(eventName: String, + preconditionPlace: Place, + preconditionTransition: (String) -> Transition?, + interactionTransition: Transition + ): Pair, List> { + + val eventTransition: Transition? = preconditionTransition(eventName) + + val notProvidedError: List = when(eventTransition) { + null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") + else -> emptyList() + } + + val arcs: List = listOf( + arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace, 1), + arc(preconditionPlace, interactionTransition, 1) + ) + + return arcs to notProvidedError + } + + // the (possible) event output arcs / places + private fun buildInteractionOutputArcs( + interaction: InteractionTransition, + eventTransitions: List) = + if (interaction.eventsToFire.isNotEmpty()) { + val resultPlace = createPlace(label = interaction.label(), placeType = Place.`InteractionEventOutputPlace$`.`MODULE$`) + val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor -> + //Get the correct event transition + val eventTransition = eventTransitions.find {it.event().name() == event.name() } ?: throw IllegalStateException("eventTransition should be found") + //Decide if there are multiple interactions that fire this transition, + // if so create a event combiner place + // else link the transition to the event. + val eventTransitionCount = eventTransitions.count { e -> e.event().name() == event.name() } + if(eventTransitionCount > 1) { + //Create a new intermediate event place + val eventCombinerPlace: Place = createPlace(label = event.name(), placeType = Place.`IntermediatePlace$`.`MODULE$`) + //Create a new intermediate event transition + val interactionToEventTransition: IntermediateTransition = IntermediateTransition("${interaction.interactionName()}:${event.name()}") + //link the interaction output place to the intermediate transition + val interactionOutputPlaceToIntermediateTransition: Arc = arc(resultPlace, interactionToEventTransition, 1, event.name()) + //link the intermediate transition to the intermediate input place + val intermediateTransitionToEventCombinerPlace: Arc = arc(interactionToEventTransition, eventCombinerPlace, 1) + //Link the intermediate place to the event place + val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) + listOf(intermediateTransitionToEventCombinerPlace, interactionOutputPlaceToIntermediateTransition, eventCombinerPlaceToEventTransition) + } + else { + val internalEventTransition = eventTransition + listOf(arc(resultPlace, internalEventTransition, 1, event.name())) + } + } + eventArcs + arc(interaction, resultPlace, 1) + } + else emptyList() + + /** + * Draws an arc from all required ingredients for an interaction + * If the ingredient has multiple consumers create a multi transition place and create both arcs for it + */ + private fun buildInteractionInputArcs( + t: InteractionTransition, + multipleConsumerFacilitatorTransitions: List, + ingredientsWithMultipleConsumers: Map>): List { + + val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = + t.nonProvidedIngredients.map(IngredientDescriptor::name).partition(ingredientsWithMultipleConsumers::contains) + + // the extra arcs to model multiple output transitions from one place + val internalDataInputArcs = fieldNamesWithPrefixMulti.flatMap { fieldName -> + val multiTransitionPlace = createPlace("${t.label()}-$fieldName", placeType = Place.`MultiTransitionPlace$`.`MODULE$`) + listOf( + // one arc from multiplier place to the transition + arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), + multiTransitionPlace, + 1), + // one arc from extra added place to transition + arc(multiTransitionPlace, t, 1) + ) + } + + + // the data input arcs / places + val dataInputArcs: List = fieldNamesWithoutPrefix.map { fieldName -> arc(createPlace(fieldName, Place.`IngredientPlace$`.`MODULE$`), t, 1) } + + val dataOutputArcs: List = + if (t.isReprovider) + fieldNamesWithoutPrefix.map { fieldName -> arc(t, createPlace(fieldName, Place.`IngredientPlace$`.`MODULE$`), 1) } + + fieldNamesWithPrefixMulti.map { fieldName -> arc(t, createPlace("${t.label()}-$fieldName", placeType = Place.`MultiTransitionPlace$`.`MODULE$`), 1) } + else + emptyList() + + val limitInteractionCountArc: List = when (val maximumInteractionCount = t.maximumInteractionCount) { + null -> emptyList() + else -> listOf(arc(createPlace("limit:${t.label()}", Place.FiringLimiterPlace(maximumInteractionCount)), t, 1)) + } + + return dataInputArcs + dataOutputArcs + internalDataInputArcs + limitInteractionCountArc + } + + private fun buildInteractionArcs( + multipleOutputFacilitatorTransitions: List, + placeNameWithDuplicateTransitions: Map>, + eventTransitions: List, interactionTransition: InteractionTransition + )= + buildInteractionInputArcs( + interactionTransition, + multipleOutputFacilitatorTransitions, + placeNameWithDuplicateTransitions + ) + buildInteractionOutputArcs( + interactionTransition, + eventTransitions + ) + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + fun compileRecipe( + recipe: Recipe, + validationSettings: ValidationSettings + ): CompiledRecipe { + + + val precompileErrors: List = preCompileAssertions(recipe) + + // Extend the interactions with the checkpoint event interactions and sub-recipes + val actionDescriptors: List = recipe.interactions + + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + recipe.sieves.map(::convertSieveToInteraction)//checkPointSieveSubRecipeInteractions(recipe) + + fun subRecipeSensoryEvents(recipe: Recipe): Set = + recipe.subRecipes.flatMap(com.ing.baker.recipe.common.Recipe::sensoryEvents).toSet() + + //All ingredient names provided by sensory events or by interactions + val allIngredientNames: Set = + (recipe.sensoryEvents + + subRecipeSensoryEvents(recipe)).flatMap { e -> e.providedIngredients.map(Ingredient::name) } + .toSet() + + actionDescriptors.flatMap { i -> + i.output.flatMap { e -> + // check if the event was renamed (check if there is a transformer for this event) + when (val transformer = i.eventOutputTransformers[e]) { + null -> e.providedIngredients.map(Ingredient::name) + else -> e.providedIngredients.map { ingredient -> + transformer.ingredientRenames[ingredient.name()] ?: ingredient.name() + } + } + } + } + + // + // For inputs for which no matching output cannot be found, we do not want to generate a place. + // It should be provided at runtime from outside the active petri net (marking) + val interactionTransitions = + actionDescriptors.map { interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) } + + val allInteractionTransitions: List = interactionTransitions + + // events provided from outside + //TODO: handle sensoryEvents subrecipe Recursion + val sensoryEventTransitions: List = (recipe.sensoryEvents + recipe.subRecipes.flatMap {it.sensoryEvents }).map { + event -> EventTransition(eventToCompiledEvent(event), true, event.maxFiringLimit()) + } + + // events provided by other transitions / actions + val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire.map { event -> EventTransition (event, false, Option.empty()) } + } + + val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions + + // Given the event classes, it is creating the ingredient places and + // connecting a transition to a ingredient place. + val internalEventArcs: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire.flatMap { event -> + event.ingredients.map { + ingredient -> + val from = interactionEventTransitions.find { it.label() == event.name() } ?: throw IllegalStateException() + arc(from, createPlace(ingredient.name(), Place.`IngredientPlace$`.`MODULE$`), 1) + } + } + } + + //Create event limiter places so that events can only fire x amount of times. + val eventLimiterArcs: List = sensoryEventTransitions.flatMap { t -> when(val n = t.maxFiringLimit().getOrElse {null as Int?}) { + null -> emptyList() + else -> listOf(arc(createPlace("limit:${t.label()}", Place.FiringLimiterPlace(n)), t, 1)) + }} + + + fun findEventTransitionByEventName(eventName: String) = allEventTransitions.find { it.event().name() == eventName } + + fun findInteractionByLabel (label: String) = allInteractionTransitions.find { it.label() == label } ?: throw IllegalStateException() + + // This generates precondition arcs for Required Events (AND). + val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { + t -> + buildEventAndPreconditionArcs( + t, + ::findEventTransitionByEventName, + ::findInteractionByLabel + ) + }.unzipFlatten() + + // This generates precondition arcs for Required Events (OR). + val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { + t -> + buildEventORPreconditionArcs( + t, + ::findEventTransitionByEventName, + ::findInteractionByLabel + ) + }.unzipFlatten() + + val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } + + // It connects a sensory event to an ingredient places + val sensoryEventArcs: List = sensoryEventWithIngredients.flatMap { et -> + et.event().ingredients.map { ingredient -> + arc(et, createPlace(ingredient.name(), Place.`IngredientPlace$`.`MODULE$`), 1) + } + } + + val eventThatArePreconditions: List = actionDescriptors.flatMap { + actionDescriptor -> actionDescriptor.requiredEvents + actionDescriptor.requiredOneOfEvents.flatten() + } + + // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net + val sensoryEventArcsNoIngredientsArcs: List = sensoryEventWithoutIngredients + //Filter out events that are preconditions to interactions + .filterNot { sensoryEvent -> eventThatArePreconditions.contains(sensoryEvent.label()) } + .map { sensoryEvent -> arc(sensoryEvent, createPlace(sensoryEvent.label(), Place.`EmptyEventIngredientPlace$`.`MODULE$`), 1) } + + // First find the cases where multiple transitions depend on the same ingredient place + val ingredientsWithMultipleConsumers: Map> = + getIngredientsWithMultipleConsumers(allInteractionTransitions) + + // Add one new transition for each duplicate input (the newly added one in the image above) + val multipleConsumerFacilitatorTransitions: List = + ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) + + val multipleOutputFacilitatorArcs: List = + multipleConsumerFacilitatorTransitions.map { t -> + arc(createPlace(t.label(), Place.`IngredientPlace$`.`MODULE$`), t, 1) } + + val interactionArcs: List = + allInteractionTransitions.flatMap { interactionTransition -> + buildInteractionArcs( + multipleConsumerFacilitatorTransitions, + ingredientsWithMultipleConsumers, + interactionEventTransitions, + interactionTransition + ) + } + + val arcs = (interactionArcs + + eventPreconditionArcs + + eventOrPreconditionArcs + + eventLimiterArcs + + sensoryEventArcs + + sensoryEventArcsNoIngredientsArcs + + internalEventArcs + + multipleOutputFacilitatorArcs) + + val petriNet: PetriNet = PetriNet(graph(arcs)) + + val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> + when (val placeType = p.placeType()) { + is FiringLimiterPlace -> + p to mapOf(null to placeType.maxLimit()) + + else -> null + } + }.toMap() + + val errors = preconditionORErrors + preconditionANDErrors + precompileErrors + + val oldRecipeIdVariant: OldRecipeIdVariant = + when (recipe) { + is com.ing.baker.recipe.javadsl.Recipe -> CompiledRecipe.`Scala212CompatibleJava$`.`MODULE$` + is com.ing.baker.recipe.kotlindsl.Recipe -> CompiledRecipe.`Scala212CompatibleKotlin$`.`MODULE$` + is com.ing.baker.recipe.scaladsl.Recipe -> CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` + else -> throw IllegalArgumentException("Only recipe of type java, kotlin or scala supported") + } + + val compiledRecipe = CompiledRecipe.build( + recipe.name(), + petriNet, + initialMarking.mapValues { it.value.mapValues{ it.value as Any}.asScala }.asScala, + errors.asScala, + recipe.eventReceivePeriod(), + recipe.retentionPeriod(), + oldRecipeIdVariant, + ) + + return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) + } + + fun compileRecipe(recipe: Recipe): CompiledRecipe = compileRecipe(recipe, `ValidationSettings$`.`MODULE$`.defaultValidationSettings()) + + + + private fun getMultiTransition(internalRepresentationName: String, transitions: List) = + transitions.find { it.label().equals(internalRepresentationName) } ?: + throw NoSuchElementException("No multi transition found with name $internalRepresentationName") + + /** + * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. + * + * @param actionTransitions Seq of reflected transition. + * @return A map from input place name to reflected transitions (where the transitions have as input the place). + */ + private fun getIngredientsWithMultipleConsumers(actionTransitions: List): Map> = + // Obtain a list of field name with their transition + actionTransitions.flatMap { transition -> + transition.nonProvidedIngredients.map { ingredient -> + ingredient.name() to transition + } + }.groupBy({ it.first}, { it.second}) + + + private fun createPlace(label: String, placeType: Place.PlaceType): Place = + Place("${placeType.labelPrepend()}$label", placeType) + + private fun convertCheckpointEventToInteraction(e: com.ing.baker.recipe.common.CheckPointEvent) = + interaction( + name = "${com.ing.baker.il.`package$`.`MODULE$`.checkpointEventInteractionPrefix()}${e.name()}", + inputIngredients = emptyList(), + output = listOf(`Event$`.`MODULE$`.apply(e.name(), emptyList().asScala)), + requiredEvents = e.requiredEvents, + requiredOneOfEvents = e.requiredOneOfEvents + ) + + private fun convertSieveToInteraction(s: com.ing.baker.recipe.common.Sieve) = + interaction( + name = "${com.ing.baker.il.`package$`.`MODULE$`.sieveInteractionPrefix()}${s.name()}", + inputIngredients = s.inputIngredients, + output = s.output, + requiredEvents = emptySet(), + requiredOneOfEvents = emptySet() + ) + + + private fun flattenSubRecipesToInteraction(recipe: com.ing.baker.recipe.common.Recipe): Set { + fun copyInteraction(i: InteractionDescriptor) = com.ing.baker.recipe.scaladsl.Interaction.apply( + "${com.ing.baker.il.`package$`.`MODULE$`.subRecipePrefix()}${recipe.name()}$$${i.name()}", + i.inputIngredients(), + i.output(), + i.requiredEvents(), + i.requiredOneOfEvents(), + i.predefinedIngredients(), + i.overriddenIngredientNames(), + i.overriddenOutputIngredientName(), + i.maximumInteractionCount(), + i.failureStrategy(), + i.eventOutputTransformers(), + i.isReprovider, + Option.apply(i.originalName()) + ) + return recipe.interactions.map(::copyInteraction).toSet() + checkPointSieveSubRecipeInteractions(recipe) + } + + private fun checkPointSieveSubRecipeInteractions(recipe: com.ing.baker.recipe.common.Recipe): List = + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction).toList() + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + recipe.sieves.map(::convertSieveToInteraction).toList() + + + +} + +fun ingredientToCompiledIngredient(ingredient: Ingredient): IngredientDescriptor = IngredientDescriptor(ingredient.name(), ingredient.ingredientType()) + +fun eventToCompiledEvent(event: Event): EventDescriptor = EventDescriptor(event.name(), event.providedIngredients.map(::ingredientToCompiledIngredient).asScala) + + +private fun graph(arcs: List): Graph> { + val defaultHints = object : Hints { + override fun initialCapacity() = 16 + override fun capacityIncrement() = 32 + override fun hashTableThreshold() = 48 + override fun compactUpToUsed() = 80 + } + + return `Graph$`.`MODULE$`.from(arcs.asScala, ClassTag.apply(WLDiEdge::class.java), CoreConfig( + GraphConfig.defaultOrder(), defaultHints + )) as Graph> +} + + +private fun wlDiEdge( + node1: Node, + node2: Node, + weight: Double, + label: L +): WLDiEdge = + `WLDiEdge$`.`MODULE$`.newEdge( + GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), + weight, + label + ) + +fun interactionTransitionOf( + interactionDescriptor: InteractionDescriptor, + defaultFailureStrategy: InteractionFailureStrategy, + allIngredientNames: Set +): InteractionTransition { + //This transforms the event using the eventOutputTransformer to the new event + //If there is no eventOutputTransformer for the event the original event is returned + fun transformEventType(event: Event): Event = + when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers.get(event)) { + null -> event + else -> com.ing.baker.recipe.scaladsl.Event( + eventOutputTransformer.newEventName, + event.providedIngredients.map { i -> + Ingredient(eventOutputTransformer.ingredientRenames.getOrElse(i.name(), { i.name() }), i.ingredientType())}.asScala, + Option.empty() + ) + } + + fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = + com.ing.baker.il.EventOutputTransformer(recipeEventOutputTransformer.newEventName(), recipeEventOutputTransformer.ingredientRenames()) + + fun transformEventToCompiledEvent(event: Event): EventDescriptor = + EventDescriptor( + event.name(), + event.providedIngredients.map(::ingredientToCompiledIngredient).asScala) + + // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe + // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe + // Replace BakerEventList to BakerEventList tag as know in compiledRecipe + // Replace ingredient tags with overridden tags + val inputFields: Seq> = interactionDescriptor.inputIngredients + .map { ingredient -> + when (ingredient.name()) { + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> com.ing.baker.il.`package$`.`MODULE$`.recipeInstanceIdName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> com.ing.baker.il.`package$`.`MODULE$`.recipeInstanceMetadataName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> com.ing.baker.il.`package$`.`MODULE$`.recipeInstanceEventListName() + else -> interactionDescriptor.overriddenIngredientNames().getOrElse(ingredient.name(), {ingredient.name()}) + } to ingredient.ingredientType() + }.asScala + + val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala + val eventsToFire = interactionDescriptor.output.map(::transformEventType).map(::transformEventToCompiledEvent).asScala + + //For each ingredient that is not provided + //And is of the type Optional or Option + //Add it to the predefinedIngredients List as empty + //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. + val predefinedIngredientsWithOptionalsEmpty: Map = + inputFields.asJava + .filter { (name, type) -> type is OptionType && name !in allIngredientNames } + .map { Pair(it.first, `NullValue$`.`MODULE$`) }.toMap() + + interactionDescriptor.predefinedIngredients + + val p: Pair> = + when (val strategy = interactionDescriptor.failureStrategy().getOrElse {defaultFailureStrategy }) { + is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { + val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { + is Some -> Some(EventDescriptor( + e.value().getOrElse { null as String? } ?: (interactionDescriptor.name() + com.ing.baker.il.`package$`.`MODULE$`.exhaustedEventAppend()), emptyList().asScala) + ) + else -> Option.empty() + } + RetryWithIncrementalBackoff(strategy.initialDelay(), strategy.backoffFactor(), strategy.maximumRetries(), strategy.maxTimeBetweenRetries(), exhaustedRetryEvent) to exhaustedRetryEvent + } + is InteractionFailureStrategy.BlockInteraction -> com.ing.baker.il.failurestrategy.`BlockInteraction$`.`MODULE$` to Option.empty() + is InteractionFailureStrategy.FireEventAfterFailure -> { + val eventName = strategy.eventName().getOrElse { interactionDescriptor.name() + com.ing.baker.il.`package$`.`MODULE$`.exhaustedEventAppend() } + val exhaustedRetryEvent: EventDescriptor = EventDescriptor(eventName, emptyList().asScala) + com.ing.baker.il.failurestrategy.FireEventAfterFailure(exhaustedRetryEvent) to Some(exhaustedRetryEvent) + } + else -> com.ing.baker.il.failurestrategy.`BlockInteraction$`.`MODULE$` to Option.empty() + } + val failureStrategy = p.first + val exhaustedRetryEvent = p.second.toList().toSeq() + + return InteractionTransition( + (eventsToFire.asJava + exhaustedRetryEvent.asJava).asScala, + (originalEvents.asJava + exhaustedRetryEvent.asJava).asScala, + inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala, + interactionDescriptor.name(), + interactionDescriptor.originalName(), + predefinedIngredientsWithOptionalsEmpty.asScala, + interactionDescriptor.maximumInteractionCount(), + failureStrategy, + interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> + event.name() to transformEventOutputTransformer( + transformer + ) + }.toMap().asScala, + interactionDescriptor.isReprovider() + ) +} + + + +private fun interaction( + name: String?, + inputIngredients: List, + output: List, + requiredEvents: kotlin.collections.Set = emptySet(), + requiredOneOfEvents: kotlin.collections.Set> = emptySet(), + predefinedIngredients: Map = emptyMap(), + overriddenIngredientNames: Map = emptyMap(), + overriddenOutputIngredientName: String? = null, + maximumInteractionCount: Int? = null, + failureStrategy: InteractionFailureStrategy? = null, + eventOutputTransformers: Map = emptyMap(), + isReprovider: Boolean = false, + oldName: String? = null, +) = + com.ing.baker.recipe.scaladsl.Interaction.apply( + name, + inputIngredients.asScala, + output.asScala, + requiredEvents.asScala, + requiredOneOfEvents.map { it: kotlin.collections.Set -> it.asScala}.toSet().asScala, + predefinedIngredients.asScala, + overriddenIngredientNames.asScala, + Option.apply(overriddenOutputIngredientName), + Option.apply(maximumInteractionCount), + Option.apply(failureStrategy), + eventOutputTransformers.asScala, + isReprovider, + Option.apply(oldName) + ) + +val Recipe.sensoryEvents get() = this.sensoryEvents().asJava +val Recipe.interactions get() = this.interactions().asJava +val Recipe.subRecipes get() = this.subRecipes().asJava +val Recipe.checkpointEvents get() = this.checkpointEvents().asJava +val Recipe.sieves get() = this.sieves().asJava +val InteractionDescriptor.requiredEvents get() = this.requiredEvents().asJava +val InteractionDescriptor.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() +val InteractionDescriptor.output get() = this.output().asJava +val InteractionDescriptor.eventOutputTransformers get() = this.eventOutputTransformers().asJava +val InteractionDescriptor.inputIngredients get() = this.inputIngredients().asJava +val InteractionDescriptor.predefinedIngredients get() = this.predefinedIngredients().asJava +val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava +val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava +val InteractionTransition.maximumInteractionCount get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } +val Event.providedIngredients get() = this.providedIngredients().asJava +val EventDescriptor.ingredients get() = this.ingredients().asJava +val EventOutputTransformer.ingredientRenames get() = this.ingredientRenames().asJava +val EventOutputTransformer.newEventName get() = this.newEventName() +val CheckPointEvent.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() +val CheckPointEvent.requiredEvents get() = this.requiredEvents().asJava +val Sieve.inputIngredients get() = this.inputIngredients().asJava +val Sieve.output get() = this.output().asJava diff --git a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt new file mode 100644 index 000000000..3970dffee --- /dev/null +++ b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt @@ -0,0 +1,20 @@ +package com.ing.baker.compiler + +import scala.`$less$colon$less` +import scala.Predef +import scala.Tuple2 +import scala.collection.immutable.Seq +import scala.jdk.CollectionConverters.* + +object ScalaConversions { + val Array.asScala get(): scala.collection.immutable.Seq = ListHasAsScala(this.toList()).asScala().toSeq() + val List.asScala get(): scala.collection.immutable.Seq = ListHasAsScala(this).asScala().toSeq() + val Set.asScala get(): scala.collection.immutable.Set = SetHasAsScala(this).asScala().toSet() + val Map.asScala get(): scala.collection.immutable.Map = MapHasAsScala(this).asScala().toMap( + Predef.`$conforms`>() as `$less$colon$less`, Tuple2>?) + + val Seq.asJava get(): List = SeqHasAsJava(this).asJava() + val scala.collection.immutable.Set.asJava get(): Set = SetHasAsJava(this).asJava() + val scala.collection.immutable.Map.asJava get(): Map = MapHasAsJava(this).asJava() +} + From 29001d9a35e53b32909a7f17c7bb93452a5ae0b3 Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Wed, 11 Mar 2026 16:19:36 +0100 Subject: [PATCH 03/16] updating Kotlin implementation of recipe compiler --- .github/workflows/ci.yml | 2 +- core/baker-compiler/pom.xml | 44 +- .../baker/compiler/PreCompileValidations.kt | 59 ++ .../baker/compiler/RecipeCompilerKotlin.kt | 803 +++++++++++++++ .../compiler/RecipeValidationException.kt | 3 + .../ing/baker/compiler/ScalaConversions.kt | 29 + .../com/ing/baker/model/KotlinBridge.kt | 79 ++ .../main/kotlin/com/ing/baker/model/Model.kt | 124 +++ .../com/ing/baker/compiler/Assertions.scala | 45 - .../ing/baker/compiler/RecipeCompiler.scala | 418 +------- .../baker/compiler/RecipeCompilerScala.scala | 416 ++++++++ .../compiler/RecipeValidationException.scala | 3 - .../baker/compiler}/RecipeCompilerTests.java | 7 +- .../baker/compiler/RecipeCompilerSpecTest.kt | 938 ++++++++++++++++++ .../ing/baker/compiler}/RecipeCompilerTest.kt | 6 +- .../baker/compiler/RecipeCompilerSpec.scala | 637 ------------ .../com/ing/baker/il/RecipeValidations.scala | 13 +- 17 files changed, 2513 insertions(+), 1113 deletions(-) create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/model/KotlinBridge.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/model/Model.kt delete mode 100644 core/baker-compiler/src/main/scala/com/ing/baker/compiler/Assertions.scala create mode 100644 core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala delete mode 100644 core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeValidationException.scala rename core/baker-compiler/src/test/java/{ => com/ing/baker/compiler}/RecipeCompilerTests.java (97%) create mode 100644 core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerSpecTest.kt rename core/baker-compiler/src/test/kotlin/{ => com/ing/baker/compiler}/RecipeCompilerTest.kt (92%) delete mode 100644 core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4de94d2a..d4cbab24f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -175,4 +175,4 @@ jobs: java-version: 21 cache: maven - name: Submit dependency graph - uses: advanced-security/maven-dependency-submission-action@v4 + uses: advanced-security/maven-dependency-submission-action@v5 diff --git a/core/baker-compiler/pom.xml b/core/baker-compiler/pom.xml index 028fa5998..ec50379f6 100644 --- a/core/baker-compiler/pom.xml +++ b/core/baker-compiler/pom.xml @@ -71,12 +71,24 @@ - + org.codehaus.mojo build-helper-maven-plugin ${build.helper.maven.plugin.version} + + add-source + generate-sources + + add-source + + + + src/main/kotlin + + + add-test-source generate-test-sources @@ -142,6 +154,36 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + false + false + false + plain + + false + + + **/*Test.class + **/*Tests.class + + + **/*Spec.class + + + + + test + + test + + + + diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt new file mode 100644 index 000000000..6824caf92 --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt @@ -0,0 +1,59 @@ +package com.ing.baker.compiler + +import com.ing.baker.model.Event +import com.ing.baker.model.Ingredient +import com.ing.baker.model.Interaction +import com.ing.baker.model.Recipe +import com.ing.baker.model.toKotlin +import scala.jdk.javaapi.CollectionConverters +import com.ing.baker.recipe.common.Recipe as ScalaRecipe + +object PreCompileValidations { + + fun preCompileAssertions(recipe: ScalaRecipe): scala.collection.immutable.Seq = + CollectionConverters.asScala(preCompileAssertions(recipe.toKotlin())).toSeq() + + fun preCompileAssertions(recipe: Recipe): List { + assertValidNames(Recipe::name, listOf(recipe), "Recipe") + assertValidNames(Interaction::name, recipe.interactions, "Interaction") + assertValidNames(Event::name, recipe.sensoryEvents, "Event") + assertValidNames(Ingredient::name, recipe.interactions.flatMap { it.inputIngredients }, "Ingredient") + assertValidNames(Ingredient::name, recipe.sensoryEvents.flatMap { it.providedIngredients }, "Ingredient") + + assertNoDuplicateElementsExist(Interaction::name, recipe.interactions) + assertNoDuplicateElementsExist(Event::name, recipe.sensoryEvents) + + return assertSensoryEventsNegativeFiringLimits(recipe) + assertRequiredEventForReprovider(recipe) + } + + private fun assertValidNames(nameFunc: (T) -> String?, list: Iterable, typeName: String) = + list.map(nameFunc) + .firstOrNull { it.isNullOrEmpty() } + ?.let { throw RecipeValidationException("$typeName with a null or empty name found") } + + private fun assertNoDuplicateElementsExist(compareIdentifier: (T) -> Any, elements: Collection) = + elements + .groupBy(compareIdentifier) + .entries + .firstOrNull { it.value.size > 1 } + ?.let { (_, duplicates) -> + val first = duplicates[0] + val second = duplicates[1] + throw RecipeValidationException( + "Duplicate identifiers found: ${first!!::class.simpleName}:$first and ${second!!::class.simpleName}:$second" + ) + } + + private fun assertSensoryEventsNegativeFiringLimits(recipe: Recipe): List = + recipe.sensoryEvents + .firstOrNull { it.maxFiringLimit != null && it.maxFiringLimit <= 0 } + ?.let { listOf("MaxFiringLimit should be greater than 0") } ?: emptyList() + + private fun assertRequiredEventForReprovider(recipe: Recipe): List = + recipe.interactions + .filter { interaction -> + interaction.isReprovider && interaction.requiredEvents.isEmpty() && interaction.requiredOneOfEvents.isEmpty() + }.map { interaction -> + "Reprovider interaction ${interaction.name} needs to have a event precondition" + } +} diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt new file mode 100644 index 000000000..5c9dc14f7 --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt @@ -0,0 +1,803 @@ +package com.ing.baker.compiler + +import com.ing.baker.compiler.PreCompileValidations.preCompileAssertions +import com.ing.baker.compiler.ScalaConversions.asJava +import com.ing.baker.compiler.ScalaConversions.asScala +import com.ing.baker.il.CompiledRecipe +import com.ing.baker.il.CompiledRecipe.OldRecipeIdVariant +import com.ing.baker.il.EventDescriptor +import com.ing.baker.il.IngredientDescriptor +import com.ing.baker.il.RecipeValidations +import com.ing.baker.il.ValidationSettings +import com.ing.baker.il.failurestrategy.`BlockInteraction$` +import com.ing.baker.il.failurestrategy.FireEventAfterFailure +import com.ing.baker.il.failurestrategy.FireFunctionalEventAfterFailure +import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff +import com.ing.baker.il.`package$` +import com.ing.baker.il.petrinet.Edge +import com.ing.baker.il.petrinet.EventTransition +import com.ing.baker.il.petrinet.InteractionTransition +import com.ing.baker.il.petrinet.IntermediateTransition +import com.ing.baker.il.petrinet.MissingEventTransition +import com.ing.baker.il.petrinet.MultiFacilitatorTransition +import com.ing.baker.il.petrinet.Place +import com.ing.baker.il.petrinet.Place.FiringLimiterPlace +import com.ing.baker.il.petrinet.Transition +import com.ing.baker.petrinet.api.PetriNet +import com.ing.baker.recipe.common.CheckPointEvent +import com.ing.baker.recipe.common.Event +import com.ing.baker.recipe.common.EventOutputTransformer +import com.ing.baker.recipe.common.Ingredient +import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.common.InteractionFailureStrategy +import com.ing.baker.recipe.common.Recipe +import com.ing.baker.recipe.common.Sieve +import com.ing.baker.recipe.scaladsl.`Event$` +import com.ing.baker.recipe.scaladsl.Interaction +import com.ing.baker.types.`NullValue$` +import com.ing.baker.types.OptionType +import com.ing.baker.types.Type +import com.ing.baker.types.Value +import scala.Option +import scala.Some +import scala.collection.immutable.Seq +import scala.reflect.ClassTag +import scala.util.Either +import scala.util.Left +import scala.util.Right +import scalax.collection.Graph +import scalax.collection.`Graph$` +import scalax.collection.GraphEdge +import scalax.collection.config.CoreConfig +import scalax.collection.config.GraphConfig +import scalax.collection.edge.WLDiEdge +import scalax.collection.edge.`WLDiEdge$` +import scalax.collection.mutable.ArraySet +import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleJava$`.`MODULE$` as Scala212CompatibleJava +import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleKotlin$`.`MODULE$` as Scala212CompatibleKotlin +import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` as Scala212CompatibleScala +import com.ing.baker.il.`ValidationSettings$`.`MODULE$` as ValidationSettings +import com.ing.baker.il.failurestrategy.InteractionFailureStrategy as ILInteractionFailureStrategy +import com.ing.baker.il.`package$`.`MODULE$` as ILPackage +import com.ing.baker.il.petrinet.Place.`EmptyEventIngredientPlace$`.`MODULE$` as EmptyEventIngredientPlace +import com.ing.baker.il.petrinet.Place.`EventPreconditionPlace$`.`MODULE$` as EventPreconditionPlace +import com.ing.baker.il.petrinet.Place.`IngredientPlace$`.`MODULE$` as IngredientPlace +import com.ing.baker.il.petrinet.Place.`InteractionEventOutputPlace$`.`MODULE$` as InteractionEventOutputPlace +import com.ing.baker.il.petrinet.Place.`IntermediatePlace$`.`MODULE$` as IntermediatePlace +import com.ing.baker.il.petrinet.Place.`MultiTransitionPlace$`.`MODULE$` as MultiTransitionPlace +import com.ing.baker.recipe.javadsl.Recipe as JavaRecipe +import com.ing.baker.recipe.kotlindsl.Recipe as KotlinRecipe +import com.ing.baker.recipe.scaladsl.Recipe as ScalaRecipe + +/** + * Type alias for the node type of the scalax.collection.Graph backing the petri net. + */ +typealias Node = Either + +/** + * Type alias for the edge type of the scalax.collection.Graph backing the petri net. + */ +typealias Arc = WLDiEdge + +/** + * Type alias for a multi set. + */ +typealias MultiSet = Map + +/** + * Type alias for a marking. + */ +typealias Marking = Map> + + +object RecipeCompilerKotlin { + fun List, List>>.unzipFlatten() = this.unzip().let { pair -> + pair.first.flatten() to pair.second.flatten() + } + + fun transition(transition: Transition) = Right(transition) + fun place(place: Place) = Left(place) + + fun arc(t: Transition, p: Place, weight: Long): Arc = + wlDiEdge(transition(t), place(p), weight.toDouble(), Edge(Option.empty())) + + fun arc(p: Place, t: Transition, weight: Long, eventFilter: String? = null): Arc = + wlDiEdge(place(p), transition(t), weight.toDouble(), Edge(Option.apply(eventFilter))) + + /** + * Creates a transition for a missing event in the recipe. + */ + private fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) + + private fun buildEventAndPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = + // Find the event in available events + interaction.requiredEvents.map { eventName -> + // a new `Place` generated for each AND events + val eventPreconditionPlace = + createPlace( + label = "$eventName-${interaction.name()}", + placeType = EventPreconditionPlace + ) + buildEventPreconditionArcs( + eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name()) + ) + }.unzipFlatten() + + private fun buildEventORPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = interaction.requiredOneOfEvents.mapIndexed { index: Int, orGroup: Set -> + // only one `Place` for all the OR events + val eventPreconditionPlace = createPlace( + label = "${interaction.name()}-or-$index", + placeType = EventPreconditionPlace + ) + orGroup.map { eventName -> + buildEventPreconditionArcs( + eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name()) + ) + }.unzipFlatten() + }.unzipFlatten() + + private fun buildEventPreconditionArcs( + eventName: String, + preconditionPlace: Place, + preconditionTransition: (String) -> Transition?, + interactionTransition: Transition + ): Pair, List> { + + val eventTransition = preconditionTransition(eventName) + + val notProvidedError = when (eventTransition) { + null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") + else -> emptyList() + } + + val arcs = listOf( + arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace, 1), + arc(preconditionPlace, interactionTransition, 1) + ) + + return arcs to notProvidedError + } + + // the (possible) event output arcs / places + private fun buildInteractionOutputArcs( + interaction: InteractionTransition, + eventTransitions: List + ) = + if (interaction.eventsToFire.isNotEmpty()) { + val resultPlace = + createPlace(label = interaction.label(), placeType = InteractionEventOutputPlace) + val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor -> + //Get the correct event transition + val eventTransition = eventTransitions.find { it.event().name() == event.name() } + ?: throw RecipeValidationException("eventTransition should be found") + //Decide if there are multiple interactions that fire this transition, + // if so create a event combiner place + // else link the transition to the event. + val eventTransitionCount = eventTransitions.count { e -> e.event().name() == event.name() } + if (eventTransitionCount > 1) { + //Create a new intermediate event place + val eventCombinerPlace: Place = + createPlace(label = event.name(), placeType = IntermediatePlace) + //Create a new intermediate event transition + val interactionToEventTransition: IntermediateTransition = + IntermediateTransition("${interaction.interactionName()}:${event.name()}") + //link the interaction output place to the intermediate transition + val interactionOutputPlaceToIntermediateTransition: Arc = + arc(resultPlace, interactionToEventTransition, 1, event.name()) + //link the intermediate transition to the intermediate input place + val intermediateTransitionToEventCombinerPlace: Arc = + arc(interactionToEventTransition, eventCombinerPlace, 1) + //Link the intermediate place to the event place + val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) + listOf( + intermediateTransitionToEventCombinerPlace, + interactionOutputPlaceToIntermediateTransition, + eventCombinerPlaceToEventTransition + ) + } else { + listOf(arc(resultPlace, eventTransition, 1, event.name())) + } + } + eventArcs + arc(interaction, resultPlace, 1) + } else emptyList() + + /** + * Draws an arc from all required ingredients for an interaction + * If the ingredient has multiple consumers create a multi transition place and create both arcs for it + */ + private fun buildInteractionInputArcs( + t: InteractionTransition, + multipleConsumerFacilitatorTransitions: List, + ingredientsWithMultipleConsumers: Map> + ): List { + + val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = + t.nonProvidedIngredients.map(IngredientDescriptor::name) + .partition(ingredientsWithMultipleConsumers::contains) + + // the extra arcs to model multiple output transitions from one place + val internalDataInputArcs = fieldNamesWithPrefixMulti.flatMap { fieldName -> + val multiTransitionPlace = + createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace) + listOf( + // one arc from multiplier place to the transition + arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), multiTransitionPlace, 1), + // one arc from extra added place to transition + arc(multiTransitionPlace, t, 1) + ) + } + + // the data input arcs / places + val dataInputArcs: List = fieldNamesWithoutPrefix.map { fieldName -> + arc(createPlace(fieldName, IngredientPlace), t, 1) + } + + val dataOutputArcs: List = + if (t.isReprovider) + fieldNamesWithoutPrefix.map { fieldName -> + arc(t, createPlace(fieldName, IngredientPlace), 1) + } + fieldNamesWithPrefixMulti.map { fieldName -> + arc(t, createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace), 1) + } + else + emptyList() + + val limitInteractionCountArc: List = when (val maximumInteractionCount = t.maximumInteractionCount) { + null -> emptyList() + else -> listOf( + arc(createPlace("limit:${t.label()}", FiringLimiterPlace(maximumInteractionCount)), t, 1) + ) + } + + return dataInputArcs + dataOutputArcs + internalDataInputArcs + limitInteractionCountArc + } + + private fun buildInteractionArcs( + multipleOutputFacilitatorTransitions: List, + placeNameWithDuplicateTransitions: Map>, + eventTransitions: List, interactionTransition: InteractionTransition + ) = + buildInteractionInputArcs( + interactionTransition, + multipleOutputFacilitatorTransitions, + placeNameWithDuplicateTransitions + ) + buildInteractionOutputArcs( + interactionTransition, + eventTransitions + ) + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + fun compileRecipe( + recipe: Recipe, + validationSettings: ValidationSettings + ): CompiledRecipe { + + val precompileErrors: List = preCompileAssertions(recipe).asJava + + // Extend the interactions with the checkpoint event interactions and sub-recipes + val actionDescriptors: List = recipe.interactions + + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + + recipe.sieves.map(::convertSieveToInteraction) + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + val sensoryEvents = flattenSensoryEvents(recipe) + + //All ingredient names provided by sensory events or by interactions + val allIngredientNames: Set = + sensoryEvents + .flatMap { e -> e.providedIngredients.map(Ingredient::name) } + .toSet() + + actionDescriptors.flatMap { i -> + i.output.flatMap { e -> + // check if the event was renamed (check if there is a transformer for this event) + when (val transformer = i.eventOutputTransformers[e]) { + null -> e.providedIngredients.map(Ingredient::name) + else -> e.providedIngredients.map { ingredient -> + transformer.ingredientRenames[ingredient.name()] ?: ingredient.name() + } + } + } + } + + // For inputs for which no matching output cannot be found, we do not want to generate a place. + // It should be provided at runtime from outside the active petri net (marking) + val allInteractionTransitions: List = + actionDescriptors.map { interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) } + + // events provided from outside + val sensoryEventTransitions: List = sensoryEvents.map { event -> + EventTransition(eventToCompiledEvent(event), true, event.maxFiringLimit()) + } + + // events provided by other transitions / actions + val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire.map { event -> EventTransition(event, false, Option.empty()) } + } + + val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions + + // Given the event classes, it is creating the ingredient places and + // connecting a transition to a ingredient place. + val internalEventArcs: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire.flatMap { event -> + event.ingredients.map { ingredient -> + val from = + interactionEventTransitions.find { it.label() == event.name() } + ?: throw RecipeValidationException() + arc(from, createPlace(ingredient.name(), IngredientPlace), 1) + } + } + } + + //Create event limiter places so that events can only fire x amount of times. + val eventLimiterArcs: List = sensoryEventTransitions.flatMap { t -> + when (val n = t.maxFiringLimit().getOrElse { null as Int? }) { + null -> emptyList() + else -> listOf(arc(createPlace("limit:${t.label()}", FiringLimiterPlace(n)), t, 1)) + } + } + + fun findEventTransitionByEventName(eventName: String) = + allEventTransitions.find { it.event().name() == eventName } + + fun findInteractionByLabel(label: String) = + allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() + + // This generates precondition arcs for Required Events (AND). + val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t -> + buildEventAndPreconditionArcs( + t, + ::findEventTransitionByEventName, + ::findInteractionByLabel + ) + }.unzipFlatten() + + // This generates precondition arcs for Required Events (OR). + val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t -> + buildEventORPreconditionArcs(t, ::findEventTransitionByEventName, ::findInteractionByLabel) + }.unzipFlatten() + + val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } + + // It connects a sensory event to an ingredient places + val sensoryEventArcs: List = sensoryEventWithIngredients.flatMap { et -> + et.event().ingredients.map { ingredient -> + arc(et, createPlace(ingredient.name(), IngredientPlace), 1) + } + } + + val eventThatArePreconditions: List = actionDescriptors.flatMap { actionDescriptor -> + actionDescriptor.requiredEvents + actionDescriptor.requiredOneOfEvents.flatten() + } + + // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net + val sensoryEventArcsNoIngredientsArcs: List = sensoryEventWithoutIngredients + //Filter out events that are preconditions to interactions + .filterNot { sensoryEvent -> eventThatArePreconditions.contains(sensoryEvent.label()) } + .map { sensoryEvent -> + arc(sensoryEvent, createPlace(sensoryEvent.label(), EmptyEventIngredientPlace), 1) + } + + // First find the cases where multiple transitions depend on the same ingredient place + val ingredientsWithMultipleConsumers: Map> = + getIngredientsWithMultipleConsumers(allInteractionTransitions) + + // Add one new transition for each duplicate input (the newly added one in the image above) + val multipleConsumerFacilitatorTransitions: List = + ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) + + val multipleOutputFacilitatorArcs: List = + multipleConsumerFacilitatorTransitions.map { t -> + arc(createPlace(t.label(), IngredientPlace), t, 1) + } + + val interactionArcs: List = + allInteractionTransitions.flatMap { interactionTransition -> + buildInteractionArcs( + multipleConsumerFacilitatorTransitions, + ingredientsWithMultipleConsumers, + interactionEventTransitions, + interactionTransition + ) + } + + val arcs = (interactionArcs + + eventPreconditionArcs + + eventOrPreconditionArcs + + eventLimiterArcs + + sensoryEventArcs + + sensoryEventArcsNoIngredientsArcs + + internalEventArcs + + multipleOutputFacilitatorArcs) + + val petriNet = PetriNet(graph(arcs)) + + val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> + when (val placeType = p.placeType()) { + is FiringLimiterPlace -> p to mapOf(null to placeType.maxLimit()) + else -> null + } + }.toMap() + + val errors = preconditionORErrors + preconditionANDErrors + precompileErrors + + val oldRecipeIdVariant: OldRecipeIdVariant = + when (recipe) { + is JavaRecipe -> Scala212CompatibleJava + is KotlinRecipe -> Scala212CompatibleKotlin + is ScalaRecipe -> Scala212CompatibleScala + else -> throw RecipeValidationException("Only recipe of type java, kotlin or scala supported") + } + + val compiledRecipe = CompiledRecipe.build( + recipe.name(), + petriNet, + initialMarking.mapValues { it.value.mapValues { it.value as Any }.asScala }.asScala, + errors.asScala, + recipe.eventReceivePeriod(), + recipe.retentionPeriod(), + oldRecipeIdVariant, + ) + + return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) + } + + fun compileRecipe(recipe: Recipe): CompiledRecipe = + compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) + + private fun getMultiTransition(internalRepresentationName: String, transitions: List) = + transitions.find { it.label().equals(internalRepresentationName) } + ?: throw NoSuchElementException("No multi transition found with name $internalRepresentationName") + + /** + * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. + * + * @param actionTransitions Seq of reflected transition. + * @return A map from input place name to reflected transitions (where the transitions have as input the place). + */ + private fun getIngredientsWithMultipleConsumers(actionTransitions: List): Map> = + // Obtain a list of field name with their transition + actionTransitions + .flatMap { transition -> + transition.nonProvidedIngredients.map { ingredient -> + ingredient.name() to transition + } + } + .groupBy({ it.first }, { it.second }) + // Only keep those place names which have more than one out-adjacent transition + .filter { (_, interactions) -> interactions.size >= 2 } + + private fun createPlace(label: String, placeType: Place.PlaceType): Place = + Place("${placeType.labelPrepend()}$label", placeType) + + private fun convertCheckpointEventToInteraction(e: CheckPointEvent) = + interaction( + name = "${`package$`.`MODULE$`.checkpointEventInteractionPrefix()}${e.name()}", + inputIngredients = emptyList(), + output = listOf(`Event$`.`MODULE$`.apply(e.name(), emptySequence().asScala)), + requiredEvents = e.requiredEvents, + requiredOneOfEvents = e.requiredOneOfEvents + ) + + private fun convertSieveToInteraction(s: Sieve) = + interaction( + name = "${`package$`.`MODULE$`.sieveInteractionPrefix()}${s.name()}", + inputIngredients = s.inputIngredients, + output = s.output, + requiredEvents = emptySet(), + requiredOneOfEvents = emptySet() + ) + + private fun flattenSubRecipesToInteraction(recipe: Recipe): Set { + fun copyInteraction(i: InteractionDescriptor) = Interaction.apply( + $$"$${`package$`.`MODULE$`.subRecipePrefix()}$${recipe.name()}$$${i.name()}", + i.inputIngredients(), + i.output(), + i.requiredEvents(), + i.requiredOneOfEvents(), + i.predefinedIngredients(), + i.overriddenIngredientNames(), + i.overriddenOutputIngredientName(), + i.maximumInteractionCount(), + i.failureStrategy(), + i.eventOutputTransformers(), + i.isReprovider, + Option.apply(i.originalName()) + ) + return recipe.interactions.map(::copyInteraction).toSet() + + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + recipe.sieves.map(::convertSieveToInteraction) + } + + private fun flattenSensoryEvents(recipe: Recipe): Set = + recipe.sensoryEvents + recipe.subRecipes.flatMap(::flattenSensoryEvents) + + private fun graph(arcs: List): Graph> = + `Graph$`.`MODULE$`.from( + arcs.map { it as WLDiEdge }.asScala, + ClassTag.apply(WLDiEdge::class.java), + CoreConfig(GraphConfig.defaultOrder(), ArraySet.`Hints$`.`MODULE$`.apply(16, 32, 48, 80)) + ) as Graph> + + private fun wlDiEdge(node1: Node, node2: Node, weight: Double, label: L): WLDiEdge = + `WLDiEdge$`.`MODULE$`.newEdge(GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), weight, label) +} + +fun ingredientToCompiledIngredient(ingredient: Ingredient): IngredientDescriptor = + IngredientDescriptor(ingredient.name(), ingredient.ingredientType()) + +fun eventToCompiledEvent(event: Event): EventDescriptor = + EventDescriptor( + event.name(), + event.providedIngredients().map(::ingredientToCompiledIngredient) as Seq + ) + +fun interactionTransitionOf( + interactionDescriptor: InteractionDescriptor, + defaultFailureStrategy: InteractionFailureStrategy, + allIngredientNames: Set +): InteractionTransition { + //This transforms the event using the eventOutputTransformer to the new event + //If there is no eventOutputTransformer for the event the original event is returned + fun transformEventType(event: Event): Event = + when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers[event]) { + null -> event + else -> com.ing.baker.recipe.scaladsl.Event( + eventOutputTransformer.newEventName, + event.providedIngredients.map { i -> + Ingredient( + eventOutputTransformer.ingredientRenames.getOrElse(i.name(), { i.name() }), + i.ingredientType() + ) + }.asScala, + Option.empty() + ) + } + + fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = + com.ing.baker.il.EventOutputTransformer( + recipeEventOutputTransformer.newEventName(), + recipeEventOutputTransformer.ingredientRenames() + ) + + fun transformEventToCompiledEvent(event: Event): EventDescriptor = + EventDescriptor( + event.name(), + event.providedIngredients().map(::ingredientToCompiledIngredient) as Seq + ) + + // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe + // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe + // Replace BakerEventList to BakerEventList tag as know in compiledRecipe + // Replace ingredient tags with overridden tags + val inputFields: Seq> = interactionDescriptor.inputIngredients + .map { ingredient -> + when (ingredient.name()) { + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> ILPackage.recipeInstanceIdName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> ILPackage.recipeInstanceMetadataName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> ILPackage.recipeInstanceEventListName() + else -> interactionDescriptor.overriddenIngredientNames() + .getOrElse(ingredient.name(), { ingredient.name() }) + } to ingredient.ingredientType() + }.asScala + + val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala + val eventsToFire = + interactionDescriptor.output.map(::transformEventType).map(::transformEventToCompiledEvent).asScala + + //For each ingredient that is not provided + //And is of the type Optional or Option + //Add it to the predefinedIngredients List as empty + //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. + val predefinedIngredientsWithOptionalsEmpty: Map = + inputFields.asJava + .filter { (name, type) -> type is OptionType && name !in allIngredientNames } + .associate { Pair(it.first, `NullValue$`.`MODULE$`) } + + interactionDescriptor.predefinedIngredients + + val p: Triple, Option> = + when (val strategy = interactionDescriptor.failureStrategy().getOrElse { defaultFailureStrategy }) { + is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { + val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { + is Some -> Some( + EventDescriptor( + e.value().getOrElse { null as String? } + ?: (interactionDescriptor.name() + ILPackage.exhaustedEventAppend()), + emptyList().asScala) + ) + + else -> Option.empty() + } + val functionalFailedEvent = when (val e = strategy.fireFunctionalEvent()) { + is Some -> Some( + EventDescriptor( + e.value().getOrElse { null as String? } + ?: (interactionDescriptor.name() + ILPackage.functionalFailedEventAppend()), + emptyList().asScala) + ) + + else -> Option.empty() + } + Triple( + RetryWithIncrementalBackoff( + strategy.initialDelay(), + strategy.backoffFactor(), + strategy.maximumRetries(), + strategy.maxTimeBetweenRetries(), + exhaustedRetryEvent, + functionalFailedEvent, + ), exhaustedRetryEvent, functionalFailedEvent + ) + } + + is InteractionFailureStrategy.BlockInteraction -> Triple( + `BlockInteraction$`.`MODULE$`, + Option.empty(), + Option.empty() + ) + + is InteractionFailureStrategy.FireEventAfterFailure -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } + val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) + Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) + } + + is InteractionFailureStrategy.FireEventAndBlock -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } + val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) + Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) + } + + is InteractionFailureStrategy.FireEventAndResolve -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name() + ILPackage.functionalFailedEventAppend() } + val functionalFailed = EventDescriptor(eventName, emptyList().asScala) + Triple(FireFunctionalEventAfterFailure(functionalFailed), Option.empty(), Some(functionalFailed)) + } + + else -> Triple(`BlockInteraction$`.`MODULE$`, Option.empty(), Option.empty()) + } + val failureStrategy = p.first + val exhaustedRetryEvent = p.second.toList().toSeq() + val functionalRetryEvent = p.third.toList().toSeq() + + // In order to keep compatibility with Scala compiler implementation, we need to use specific Scala collections. + // Otherwise, generated recipe id's hash would be different between the two implementations, which is not desirable. + fun concatEvents(first: Seq, vararg rest: Seq): Seq { + // Handling special case for checkpoint events to keep it compatible with RecipeCompiler Scala implementation. + return if (interactionDescriptor.isSpecialScalaCaseSeq() || first.isEmpty) + scala.collection.immutable.`$colon$colon`.from( + first.toList() + ) + else + rest.fold(first.asJava) { acc, list -> + acc + list.asJava + }.asScala + } + + val eventsToFireAll = concatEvents(eventsToFire, exhaustedRetryEvent, functionalRetryEvent) + val originalEventsAll = concatEvents(originalEvents, exhaustedRetryEvent, functionalRetryEvent) + + // Here the same - keeping recipe id's hash the same as Scala implementation. + fun requiredIngredients(): Seq { + return if (interactionDescriptor.isSpecialScalaCaseSeq() && inputFields.isEmpty()) + scala.collection.immutable.`Nil$`.`MODULE$` as Seq + else if (interactionDescriptor.isSpecialScalaCaseSeq() || (eventsToFireAll.isEmpty() && originalEventsAll.isEmpty())) + scala.collection.immutable.List.from( + inputFields + .asJava + .map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) } + .asScala + ) + .`$plus$plus`(scala.collection.immutable.`Nil$`.`MODULE$` as Seq) as Seq + else + inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala + } + + return InteractionTransition( + eventsToFireAll, + originalEventsAll, + requiredIngredients(), + interactionDescriptor.name(), + interactionDescriptor.originalName(), + predefinedIngredientsWithOptionalsEmpty.asScala, + interactionDescriptor.maximumInteractionCount(), + failureStrategy, + interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> + event.name() to transformEventOutputTransformer( + transformer + ) + }.toMap().asScala, + interactionDescriptor.isReprovider() + ) +} + +private fun InteractionDescriptor.isSpecialScalaCaseSeq() = isCheckpoint() || isSieve() || isSubRecipe() + +private fun InteractionDescriptor.isCheckpoint() = + name().startsWith(`package$`.`MODULE$`.checkpointEventInteractionPrefix()) + +private fun InteractionDescriptor.isSieve() = + name().startsWith("SieveInteraction") || + name().startsWith(`package$`.`MODULE$`.sieveInteractionPrefix()) + +private fun InteractionDescriptor.isSubRecipe() = + name().startsWith(`package$`.`MODULE$`.subRecipePrefix()) + +private fun interaction( + name: String?, + inputIngredients: List, + output: List, + requiredEvents: Set = emptySet(), + requiredOneOfEvents: Set> = emptySet(), + predefinedIngredients: Map = emptyMap(), + overriddenIngredientNames: Map = emptyMap(), + overriddenOutputIngredientName: String? = null, + maximumInteractionCount: Int? = null, + failureStrategy: InteractionFailureStrategy? = null, + eventOutputTransformers: Map = emptyMap(), + isReprovider: Boolean = false, + oldName: String? = null, +) = + Interaction.apply( + name, + inputIngredients.asScala, + output.asScala, + requiredEvents.asScala, + requiredOneOfEvents.map { it: Set -> it.asScala }.toSet().asScala, + predefinedIngredients.asScala, + overriddenIngredientNames.asScala, + Option.apply(overriddenOutputIngredientName), + Option.apply(maximumInteractionCount), + Option.apply(failureStrategy), + eventOutputTransformers.asScala, + isReprovider, + Option.apply(oldName) + ) + +val Recipe.sensoryEvents get() = this.sensoryEvents().asJava +val Recipe.interactions get() = this.interactions().asJava +val Recipe.subRecipes get() = this.subRecipes().asJava +val Recipe.checkpointEvents get() = this.checkpointEvents().asJava +val Recipe.sieves get() = this.sieves().asJava +val InteractionDescriptor.requiredEvents get() = this.requiredEvents().asJava +val InteractionDescriptor.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() +val InteractionDescriptor.output get() = this.output().asJava +val InteractionDescriptor.eventOutputTransformers get() = this.eventOutputTransformers().asJava +val InteractionDescriptor.inputIngredients get() = this.inputIngredients().asJava +val InteractionDescriptor.predefinedIngredients get() = this.predefinedIngredients().asJava +val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava +val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava +val InteractionTransition.maximumInteractionCount + get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } +val Event.providedIngredients get() = this.providedIngredients().asJava +val EventDescriptor.ingredients get() = this.ingredients().asJava +val EventOutputTransformer.ingredientRenames get() = this.ingredientRenames().asJava +val EventOutputTransformer.newEventName: String? get() = this.newEventName() +val CheckPointEvent.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() +val CheckPointEvent.requiredEvents get() = this.requiredEvents().asJava +val Sieve.inputIngredients get() = this.inputIngredients().asJava +val Sieve.output get() = this.output().asJava diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt new file mode 100644 index 000000000..85b95dc94 --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt @@ -0,0 +1,3 @@ +package com.ing.baker.compiler + +data class RecipeValidationException(val reason: String = ""): IllegalArgumentException(reason) diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt new file mode 100644 index 000000000..08fa4702c --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt @@ -0,0 +1,29 @@ +package com.ing.baker.compiler + +import scala.`$less$colon$less` +import scala.Predef +import scala.Tuple2 +import scala.collection.immutable.ArraySeq +import scala.collection.immutable.Seq +import scala.collection.immutable.Vector +import scala.jdk.CollectionConverters.ListHasAsScala +import scala.jdk.CollectionConverters.MapHasAsJava +import scala.jdk.CollectionConverters.MapHasAsScala +import scala.jdk.CollectionConverters.SeqHasAsJava +import scala.jdk.CollectionConverters.SetHasAsJava +import scala.jdk.CollectionConverters.SetHasAsScala + +object ScalaConversions { + + inline val List.asScala get(): ArraySeq = ArraySeq.unsafeWrapArray(toTypedArray()) + inline val Sequence.asScala get(): Vector = Vector.from(this.toList().asScala) + + val Array.asScala get(): Seq = ListHasAsScala(this.toList()).asScala().toSeq() + val Set.asScala get(): scala.collection.immutable.Set = SetHasAsScala(this).asScala().toSet() + val Map.asScala get(): scala.collection.immutable.Map = MapHasAsScala(this).asScala().toMap( + Predef.`$conforms`>() as `$less$colon$less`, Tuple2>?) + + val Seq.asJava get(): List = SeqHasAsJava(this).asJava() + val scala.collection.immutable.Set.asJava get(): Set = SetHasAsJava(this).asJava() + val scala.collection.immutable.Map.asJava get(): Map = MapHasAsJava(this).asJava() +} diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/model/KotlinBridge.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/model/KotlinBridge.kt new file mode 100644 index 000000000..d58bc0b31 --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/model/KotlinBridge.kt @@ -0,0 +1,79 @@ +package com.ing.baker.model + +import scala.concurrent.duration.FiniteDuration +import kotlin.collections.mapKeys +import com.ing.baker.recipe.common.Event as ScalaEvent +import com.ing.baker.recipe.common.Recipe as ScalaRecipe +import com.ing.baker.recipe.common.Sieve as ScalaSieve +import com.ing.baker.recipe.common.Ingredient as ScalaIngredient +import com.ing.baker.recipe.common.EventOutputTransformer as ScalaEventOutputTransformer +import com.ing.baker.recipe.common.InteractionDescriptor as ScalaInteraction +import com.ing.baker.recipe.common.CheckPointEvent as ScalaCheckPointEvent +import scala.jdk.javaapi.CollectionConverters +import kotlin.time.Duration +import java.time.Duration as JavaDuration +import kotlin.time.toKotlinDuration + +fun FiniteDuration.toKotlin(): Duration = JavaDuration.ofNanos(toNanos()).toKotlinDuration() + +fun ScalaRecipe.toKotlin(): Recipe = + Recipe( + name = name().orEmpty(), + interactions = CollectionConverters.asJava(interactions()).toList().map { it.toKotlin() }, + sensoryEvents = CollectionConverters.asJava(sensoryEvents()).map { it.toKotlin() }.toSet(), + subRecipes = CollectionConverters.asJava(subRecipes()).map { it.toKotlin() }.toSet(), + defaultFailureStrategy = defaultFailureStrategy(), + eventReceivePeriod = eventReceivePeriod().map { it.toKotlin() }.getOrElse { null }, + retentionPeriod = retentionPeriod().map { it.toKotlin() }.getOrElse { null }, + checkpointEvents = CollectionConverters.asJava(checkpointEvents()).map { it.toKotlin() }.toSet(), + sieves = CollectionConverters.asJava(this.sieves()).map { it.toKotlin() }.toSet(), + ) + +fun ScalaInteraction.toKotlin(): Interaction = + Interaction( + name = name().orEmpty(), + originalName = originalName().orEmpty(), + inputIngredients = CollectionConverters.asJava(inputIngredients()).toList().map { it.toKotlin() }, + output = CollectionConverters.asJava(output()).map { it.toKotlin() }, + requiredEvents = CollectionConverters.asJava(requiredEvents()).toSet(), + requiredOneOfEvents = CollectionConverters.asJava(requiredOneOfEvents()).toSet() + .map { CollectionConverters.asJava(it).toSet() }.toSet(), + predefinedIngredients = CollectionConverters.asJava(predefinedIngredients()).toMap(), + overriddenIngredientNames = CollectionConverters.asJava(overriddenIngredientNames()).toMap(), + overriddenOutputIngredientName = overriddenOutputIngredientName().getOrElse { null }, + eventOutputTransformers = CollectionConverters.asJava(eventOutputTransformers()).toMap() + .mapKeys { it.key.toKotlin() } + .mapValues { it.value.toKotlin() }, + maximumInteractionCount = maximumInteractionCount().getOrElse { null }, + failureStrategy = failureStrategy().getOrElse { null }, + isReprovider = isReprovider() + ) + + +fun ScalaIngredient.toKotlin(): Ingredient = Ingredient(name = name().orEmpty(), type = ingredientType()) + +fun ScalaSieve.toKotlin(): Sieve = Sieve( + name = name().orEmpty(), + inputIngredients = CollectionConverters.asJava(inputIngredients()).toList().map { it.toKotlin() }, + output = CollectionConverters.asJava(output()).map { it.toKotlin() }, + function = function(), + javaTypes = CollectionConverters.asJava(javaTypes()).toList() +) + +fun ScalaCheckPointEvent.toKotlin(): CheckPointEvent = CheckPointEvent( + name = name().orEmpty(), + requiredEvents = CollectionConverters.asJava(requiredEvents()).toSet(), + requiredOneOfEvents = CollectionConverters.asJava(requiredOneOfEvents()).toSet() + .map { CollectionConverters.asJava(it).toSet() }.toSet() +) + +fun ScalaEventOutputTransformer.toKotlin(): EventOutputTransformer = EventOutputTransformer( + newEventName = newEventName(), + ingredientRenames = CollectionConverters.asJava(ingredientRenames()).toMap() +) + +fun ScalaEvent.toKotlin(): Event = Event( + name = name().orEmpty(), + providedIngredients = CollectionConverters.asJava(providedIngredients()).toList().map { it.toKotlin() }, + maxFiringLimit = maxFiringLimit().getOrElse { null } +) diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/model/Model.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/model/Model.kt new file mode 100644 index 000000000..64801f784 --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/model/Model.kt @@ -0,0 +1,124 @@ +package com.ing.baker.model + +import com.ing.baker.recipe.common.InteractionFailureStrategy +import com.ing.baker.types.Type +import kotlin.collections.mapValues +import kotlin.collections.toMap +import kotlin.time.Duration +import java.lang.reflect.Type as JavaType +import com.ing.baker.types.Converters +import com.ing.baker.types.Value + +data class Event( + val name: String, + val providedIngredients: List, + val maxFiringLimit: Int? = null, +) + +data class CheckPointEvent( + val name: String = "", + val requiredEvents: Set = emptySet(), + val requiredOneOfEvents: Set> = emptySet(), +) + +data class Ingredient(val name: String, val type: Type) + +data class Recipe( + val name: String, + val interactions: List, + val sensoryEvents: Set, + val subRecipes: Set, + val defaultFailureStrategy: InteractionFailureStrategy, + val eventReceivePeriod: Duration? = null, + val retentionPeriod: Duration? = null, + val checkpointEvents: Set, + val sieves: Set, +) + +data class Sieve( + val name: String = "", + val inputIngredients: List, + val output: List, + val function: Any, + val javaTypes: List +) + +data class EventOutputTransformer(val newEventName: String, val ingredientRenames: Map) + +data class Interaction( + val name: String, + val originalName: String, + val inputIngredients: List, + val output: List, + val requiredEvents: Set, + val requiredOneOfEvents: Set>, + val predefinedIngredients: Map, + val overriddenIngredientNames: Map, + val overriddenOutputIngredientName: String? = null, + val eventOutputTransformers: Map, + val maximumInteractionCount: Int? = null, + val failureStrategy: InteractionFailureStrategy? = null, + val isReprovider: Boolean +) { + companion object { + @JvmStatic + fun of( + name: String, + interaction: Interaction, + requiredEvents: Set, + requiredOneOfEvents: Set>, + predefinedIngredients: Map, + overriddenIngredientNames: Map, + eventOutputTransformers: Map, + maximumInteractionCount: Int?, + failureStrategy: InteractionFailureStrategy?, + isReprovider: Boolean, + ): Interaction = + Interaction( + name, + interaction.originalName, + interaction.inputIngredients, + interaction.output, + requiredEvents, + requiredOneOfEvents, + predefinedIngredients.mapValues { Converters.toValue(it.value) }, + overriddenIngredientNames, + overriddenOutputIngredientName = null, + eventOutputTransformers, + maximumInteractionCount, + failureStrategy, + isReprovider + ) + + @JvmStatic + fun of( + name: String, + originalName: String, + inputIngredients: Set, + output: Set, + requiredEvents: Set, + requiredOneOfEvents: Set>, + predefinedIngredients: Map, + overriddenIngredientNames: Map, + eventOutputTransformers: Map, + maximumInteractionCount: Int?, + failureStrategy: InteractionFailureStrategy?, + isReprovider: Boolean + ): Interaction = + Interaction( + name, + originalName, + inputIngredients.toList(), + output.toList(), + requiredEvents, + requiredOneOfEvents, + predefinedIngredients.mapValues { Converters.toValue(it.value) }, + overriddenIngredientNames.toMap(), + overriddenOutputIngredientName = null, + eventOutputTransformers, + maximumInteractionCount, + failureStrategy, + isReprovider + ) + } +} diff --git a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/Assertions.scala b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/Assertions.scala deleted file mode 100644 index 4ece7fff5..000000000 --- a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/Assertions.scala +++ /dev/null @@ -1,45 +0,0 @@ -package com.ing.baker.compiler - -import com.ing.baker.recipe.common.{Event, Ingredient, InteractionDescriptor, Recipe} - -import scala.collection.immutable.Seq - -object Assertions { - private def assertNoDuplicateElementsExist[T](compareIdentifier: T => Any, elements: Set[T]): Unit = elements.foreach { e => - (elements - e).find(c => compareIdentifier(c) == compareIdentifier(e)).foreach { c => throw new IllegalStateException(s"Duplicate identifiers found: ${e.getClass.getSimpleName}:$e and ${c.getClass.getSimpleName}:$c") } - } - - private def assertValidNames[T](nameFunc: T => String, list: Iterable[T], typeName: String): Unit = list.map(nameFunc).filter(name => name == null || name.isEmpty).foreach { _ => - throw new IllegalArgumentException(s"$typeName with a null or empty name found") - } - - private def assertNonEmptyRecipe(recipe: Recipe): Seq[String] = - Seq( - Some("No sensory events found.").filter(_ => recipe.sensoryEvents.isEmpty), - Some("No interactions found.").filter(_ => recipe.interactions.isEmpty) - ).flatten - - private def assertRequiredEventForReprovider(recipe: Recipe): Seq[String] = { - recipe.interactions.filter(interaction => { - interaction.isReprovider && interaction.requiredEvents.isEmpty && interaction.requiredOneOfEvents.isEmpty - }).map(interaction => s"Reprovider interaction ${interaction.name} needs to have a event precondition") - } - - private def assertSensoryEventsNegativeFiringLimits(recipe: Recipe): Seq[String] = - Seq( - Some("MaxFiringLimit should be greater than 0").filter(_ => - recipe.sensoryEvents.flatMap(_.maxFiringLimit).exists(_ <= 0) - ), - ).flatten - - def preCompileAssertions(recipe: Recipe): Seq[String] = { - assertValidNames[Recipe](_.name, Seq(recipe), "Recipe") - assertValidNames[InteractionDescriptor](_.name, recipe.interactions, "Interaction") - assertValidNames[Event](_.name, recipe.sensoryEvents, "Event") - val allIngredients = recipe.sensoryEvents.flatMap(_.providedIngredients) ++ recipe.interactions.flatMap(_.inputIngredients) - assertValidNames[Ingredient](_.name, allIngredients, "Ingredient") - assertNoDuplicateElementsExist[InteractionDescriptor](_.name, recipe.interactions.toSet) - assertNoDuplicateElementsExist[Event](_.name, recipe.sensoryEvents) - assertSensoryEventsNegativeFiringLimits(recipe) ++ assertRequiredEventForReprovider(recipe) - } -} diff --git a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala index ba1eade1e..49caab906 100644 --- a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala +++ b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala @@ -1,424 +1,16 @@ package com.ing.baker package compiler -import com.ing.baker.il.CompiledRecipe.{OldRecipeIdVariant, Scala212CompatibleJava, Scala212CompatibleKotlin, Scala212CompatibleScala} -import com.ing.baker.il.RecipeValidations.postCompileValidations -import com.ing.baker.il.petrinet.Place._ -import com.ing.baker.il.petrinet._ -import com.ing.baker.il.{CompiledRecipe, EventDescriptor, ValidationSettings, checkpointEventInteractionPrefix, sieveInteractionPrefix, subRecipePrefix} -import com.ing.baker.petrinet.api._ +import com.ing.baker.il.{CompiledRecipe, ValidationSettings} import com.ing.baker.recipe.common._ -import com.ing.baker.recipe.{javadsl, kotlindsl} -import com.ing.baker.recipe.scaladsl.{Event, Interaction} -import scalax.collection.edge.WLDiEdge -import scalax.collection.immutable.Graph -import scala.annotation.nowarn import scala.language.postfixOps object RecipeCompiler { - implicit class TupleSeqOps[A, B](seq: Seq[(Seq[A], Seq[B])]) { - def unzipFlatten: (Seq[A], Seq[B]) = seq.unzip match { - case (a, b) => (a.flatten, b.flatten) - } - } + def compileRecipe(recipe: Recipe): CompiledRecipe = + compileRecipe(recipe, ValidationSettings.defaultValidationSettings) - @nowarn - def arc(t: Transition, p: Place, weight: Long): Arc = WLDiEdge[Node, Edge](Right(t), Left(p))(weight, Edge(None)) - - @nowarn - def arc(p: Place, t: Transition, weight: Long, eventFilter: Option[String] = None): Arc = { - WLDiEdge[Node, Edge](Left(p), Right(t))(weight, Edge(eventFilter)) - } - - /** - * Creates a transition for a missing event in the recipe. - */ - private def missingEventTransition[E](eventName: String): MissingEventTransition = MissingEventTransition(eventName) - - private def buildEventAndPreconditionArcs(interaction: InteractionDescriptor, - preconditionTransition: String => Option[Transition], - interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { - - //Find the event in available events - - interaction.requiredEvents.toIndexedSeq.map { eventName => - // a new `Place` generated for each AND events - val eventPreconditionPlace = createPlace(label = s"$eventName-${interaction.name}", placeType = EventPreconditionPlace) - - buildEventPreconditionArcs(eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name).get) - }.unzipFlatten - } - - private def buildEventORPreconditionArcs(interaction: InteractionDescriptor, - preconditionTransition: String => Option[Transition], - interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { - - interaction.requiredOneOfEvents.toIndexedSeq.zipWithIndex.map { case (orGroup: Set[String], index: Int) => - // only one `Place` for all the OR events - val eventPreconditionPlace = createPlace(label = s"${interaction.name}-or-$index", placeType = EventOrPreconditionPlace) - - orGroup.toIndexedSeq.map { eventName => - buildEventPreconditionArcs(eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name).get) - }.unzipFlatten - }.unzipFlatten - } - - private def buildEventPreconditionArcs(eventName: String, - preconditionPlace: Place, - preconditionTransition: String => Option[Transition], - interactionTransition: Transition): (Seq[Arc], Seq[String]) = { - - val eventTransition = preconditionTransition(eventName) - - val notProvidedError = eventTransition match { - case None => Seq(s"Event '$eventName' for '$interactionTransition' is not provided in the recipe") - case Some(_) => Seq.empty - } - - val arcs = Seq( - arc(eventTransition.getOrElse(missingEventTransition(eventName)), preconditionPlace, 1), - arc(preconditionPlace, interactionTransition, 1) - ) - - (arcs, notProvidedError) - } - - // the (possible) event output arcs / places - private def buildInteractionOutputArcs(interaction: InteractionTransition, - eventTransitions: Seq[EventTransition]): Seq[Arc] = { - val resultPlace = createPlace(label = interaction.label, placeType = InteractionEventOutputPlace) - if (interaction.eventsToFire.nonEmpty) { - val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor => - //Get the correct event transition - val eventTransition = eventTransitions.find(_.event.name == event.name).get - //Decide if there are multiple interactions that fire this transition, - // if so create a event combiner place - // else link the transition to the event. - val eventTransitionCount = eventTransitions.count(e => e.event.name == event.name) - if(eventTransitionCount > 1) { - //Create a new intermediate event place - val eventCombinerPlace: Place = createPlace(label = event.name, placeType = IntermediatePlace) - //Create a new intermediate event transition - val interactionToEventTransition: IntermediateTransition = IntermediateTransition(s"${interaction.interactionName}:${event.name}") - //link the interaction output place to the intermediate transition - val interactionOutputPlaceToIntermediateTransition: Arc = arc(resultPlace, interactionToEventTransition, 1, Some(event.name)) - //link the intermediate transition to the intermediate input place - val intermediateTransitionToEventCombinerPlace: Arc = arc(interactionToEventTransition, eventCombinerPlace, 1) - //Link the intermediate place to the event place - val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) - Seq(intermediateTransitionToEventCombinerPlace, interactionOutputPlaceToIntermediateTransition, eventCombinerPlaceToEventTransition) - } - else { - val internalEventTransition = eventTransition - Seq(arc(resultPlace, internalEventTransition, 1, Some(event.name))) - } - } - arc(interaction, resultPlace, 1) +: eventArcs - } - else Seq.empty - } - - - /** - * Draws an arc from all required ingredients for an interaction - * If the ingredient has multiple consumers create a multi transition place and create both arcs for it - */ - private def buildInteractionInputArcs(t: InteractionTransition, - multipleConsumerFacilitatorTransitions: Seq[Transition], - ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]]): Seq[Arc] = { - - val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = - t.nonProvidedIngredients.map(_.name).partition(ingredientsWithMultipleConsumers.contains) - - // the extra arcs to model multiple output transitions from one place - val internalDataInputArcs: Seq[Arc] = fieldNamesWithPrefixMulti flatMap { fieldName => - val multiTransitionPlace = createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace) - Seq( - // one arc from multiplier place to the transition - arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), - multiTransitionPlace, - 1), - // one arc from extra added place to transition - arc(multiTransitionPlace, t, 1)) - } - - - // the data input arcs / places - val dataInputArcs: Seq[Arc] = fieldNamesWithoutPrefix.map(fieldName => arc(createPlace(fieldName, IngredientPlace), t, 1)) - - val dataOutputArcs: Seq[Arc] = - if(t.isReprovider) - fieldNamesWithoutPrefix.map(fieldName => arc(t, createPlace(fieldName, IngredientPlace), 1)) ++ - fieldNamesWithPrefixMulti.map(fieldName => arc(t, createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace), 1)) - else - Seq.empty - - val limitInteractionCountArc: Option[Arc] = - t.maximumInteractionCount.map(n => arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) - - dataInputArcs ++ dataOutputArcs ++ internalDataInputArcs ++ limitInteractionCountArc - } - - private def buildInteractionArcs(multipleOutputFacilitatorTransitions: Seq[Transition], - placeNameWithDuplicateTransitions: Map[String, Seq[InteractionTransition]], - eventTransitions: Seq[EventTransition]) - (t: InteractionTransition): Seq[Arc] = { - - val inputArcs: Seq[Arc] = buildInteractionInputArcs( - t, - multipleOutputFacilitatorTransitions, - placeNameWithDuplicateTransitions) - - val outputArcs: Seq[Arc] = buildInteractionOutputArcs(t, eventTransitions) - - inputArcs ++ outputArcs - } - - /** - * Compile the given recipe to a technical recipe that is useful for Baker. - * - * @param recipe ; The Recipe to compile and execute - * @param validationSettings The validation settings to follow for the validation - * @return - */ - def compileRecipe(recipe: Recipe, - validationSettings: ValidationSettings): CompiledRecipe = { - - def convertCheckpointEventToInteraction(e: CheckPointEvent) = - Interaction( - name = s"${checkpointEventInteractionPrefix}${e.name}", - inputIngredients = Seq.empty, - output = Seq(Event(e.name)), - requiredEvents = e.requiredEvents, - requiredOneOfEvents = e.requiredOneOfEvents) - - def convertSieveToInteraction(s: Sieve) = - Interaction( - name = s"${sieveInteractionPrefix}${s.name}", - inputIngredients = s.inputIngredients, - output = s.output, - requiredEvents = Set.empty, - requiredOneOfEvents = Set.empty - ) - - def flattenSubRecipesToInteraction(recipe: Recipe): Set[InteractionDescriptor] = { - def copyInteraction(i: InteractionDescriptor) = Interaction( - name = s"${subRecipePrefix}${recipe.name}$$${i.name}", - inputIngredients = i.inputIngredients, - output = i.output, - requiredEvents = i.requiredEvents, - requiredOneOfEvents = i.requiredOneOfEvents, - predefinedIngredients = i.predefinedIngredients, - overriddenIngredientNames = i.overriddenIngredientNames, - overriddenOutputIngredientName = i.overriddenOutputIngredientName, - maximumInteractionCount = i.maximumInteractionCount, - failureStrategy = i.failureStrategy, - eventOutputTransformers = i.eventOutputTransformers, - isReprovider = i.isReprovider, - oldName = Option(i.originalName) - ) - recipe.interactions.map(copyInteraction).toSet ++ - recipe.sieves.map(convertSieveToInteraction) ++ - recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ - recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) - } - - def flattenSensoryEvents(recipe: Recipe): Set[com.ing.baker.recipe.common.Event] = { - recipe.sensoryEvents ++ recipe.subRecipes.flatMap(flattenSensoryEvents) - } - - val precompileErrors: Seq[String] = Assertions.preCompileAssertions(recipe) - - // Extend the interactions with the checkpoint event interactions and sub-recipes - val actionDescriptors: Seq[InteractionDescriptor] = recipe.interactions ++ - recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ - recipe.sieves.map(convertSieveToInteraction) ++ - recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) - - // Flatten all sensory events from sub recipes - val sensoryEvents = flattenSensoryEvents(recipe) - - //All ingredient names provided by sensory events or by interactions - val allIngredientNames: Set[String] = - sensoryEvents.flatMap(e => e.providedIngredients.map(i => i.name)) ++ - actionDescriptors.flatMap(i => i.output.flatMap { e => - // check if the event was renamed (check if there is a transformer for this event) - i.eventOutputTransformers.get(e) match { - case Some(transformer) => e.providedIngredients.map(ingredient => transformer.ingredientRenames.getOrElse(ingredient.name, ingredient.name)) - case None => e.providedIngredients.map(_.name) - } - } - ) - - // For inputs for which no matching output cannot be found, we do not want to generate a place. - // It should be provided at runtime from outside the active petri net (marking) - val interactionTransitions = actionDescriptors.map(_.toInteractionTransition(recipe.defaultFailureStrategy, allIngredientNames)) - - val allInteractionTransitions: Seq[InteractionTransition] = interactionTransitions - - // events provided from outside - val sensoryEventTransitions: Seq[EventTransition] = sensoryEvents.map { - event => EventTransition(eventToCompiledEvent(event), isSensoryEvent = true, event.maxFiringLimit) - }.toIndexedSeq - - // events provided by other transitions / actions - val interactionEventTransitions: Seq[EventTransition] = allInteractionTransitions.flatMap { t => - t.eventsToFire.map(event => EventTransition(event, isSensoryEvent = false)) - } - - val allEventTransitions: Seq[EventTransition] = sensoryEventTransitions ++ interactionEventTransitions - - // Given the event classes, it is creating the ingredient places and - // connecting a transition to a ingredient place. - val internalEventArcs: Seq[Arc] = allInteractionTransitions.flatMap { t => - t.eventsToFire.flatMap { event => - event.ingredients.map { ingredient => - val from = interactionEventTransitions.find(_.label == event.name).get - arc(from, createPlace(ingredient.name, IngredientPlace), 1) - } - } - } - - //Create event limiter places so that events can only fire x amount of times. - val eventLimiterArcs: Seq[Arc] = sensoryEventTransitions.flatMap( - t => t.maxFiringLimit match { - case Some(n) => Seq(arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) - case None => Seq.empty - } - ) - - def findEventTransitionByEventName(eventName: String) = allEventTransitions.find(_.event.name == eventName) - - def findInteractionByLabel(label: String) = allInteractionTransitions.find(_.label == label) - - // This generates precondition arcs for Required Events (AND). - val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t => - buildEventAndPreconditionArcs(t, - findEventTransitionByEventName, - findInteractionByLabel) - }.unzipFlatten - - // This generates precondition arcs for Required Events (OR). - val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t => - buildEventORPreconditionArcs(t, - findEventTransitionByEventName, - findInteractionByLabel) - }.unzipFlatten - - val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition(_.event.ingredients.isEmpty) - - // It connects a sensory event to an ingredient places - val sensoryEventArcs: Seq[Arc] = sensoryEventWithIngredients - .flatMap(et => et.event.ingredients.map(ingredient => arc(et, createPlace(ingredient.name, IngredientPlace), 1))) - - val eventThatArePreconditions: Seq[String] = - actionDescriptors.flatMap { - actionDescriptor => actionDescriptor.requiredEvents ++ actionDescriptor.requiredOneOfEvents.flatten - } - - // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net - val sensoryEventArcsNoIngredientsArcs: Seq[Arc] = sensoryEventWithoutIngredients - //Filter out events that are preconditions to interactions - .filterNot(sensoryEvent => eventThatArePreconditions.contains(sensoryEvent.label)) - .map(sensoryEvent => arc(sensoryEvent, createPlace(sensoryEvent.label, EmptyEventIngredientPlace), 1)) - - // First find the cases where multiple transitions depend on the same ingredient place - val ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]] = - getIngredientsWithMultipleConsumers(allInteractionTransitions) - - // Add one new transition for each duplicate input (the newly added one in the image above) - val multipleConsumerFacilitatorTransitions: Seq[Transition] = - ingredientsWithMultipleConsumers.keys - .map(name => MultiFacilitatorTransition(label = name)) - .toIndexedSeq - - val multipleOutputFacilitatorArcs: Seq[Arc] = - multipleConsumerFacilitatorTransitions.map(t => - arc(createPlace(t.label, IngredientPlace), t, 1)) - - val interactionArcs: Seq[Arc] = - allInteractionTransitions.flatMap( - buildInteractionArcs( - multipleConsumerFacilitatorTransitions, - ingredientsWithMultipleConsumers, - interactionEventTransitions)) - - val arcs = (interactionArcs - ++ eventPreconditionArcs - ++ eventOrPreconditionArcs - ++ eventLimiterArcs - ++ sensoryEventArcs - ++ sensoryEventArcsNoIngredientsArcs - ++ internalEventArcs - ++ multipleOutputFacilitatorArcs) - - val petriNet: PetriNet = new PetriNet(Graph(arcs: _*)) - - val initialMarking: Marking[Place] = petriNet.places.collect { - case p @ Place(_, FiringLimiterPlace(n)) => p -> Map[Any, Int]((null, n)) - }.toMarking - - val errors = preconditionORErrors ++ preconditionANDErrors ++ precompileErrors - - val oldRecipeIdVariant : OldRecipeIdVariant = - recipe match { - case _: javadsl.Recipe => Scala212CompatibleJava - case _: kotlindsl.Recipe => Scala212CompatibleKotlin - case _ => Scala212CompatibleScala - } - - val compiledRecipe = CompiledRecipe.build( - name = recipe.name, - petriNet = petriNet, - initialMarking = initialMarking, - validationErrors = errors, - eventReceivePeriod = recipe.eventReceivePeriod, - retentionPeriod = recipe.retentionPeriod, - oldRecipeIdVariant = oldRecipeIdVariant, - ) - - postCompileValidations(compiledRecipe, validationSettings) - } - - def compileRecipe(recipe: Recipe): CompiledRecipe = compileRecipe(recipe, ValidationSettings.defaultValidationSettings) - - private def getMultiTransition(internalRepresentationName: String, - transitions: Seq[Transition]): Transition = { - transitions - .find(t => t.label.equals(internalRepresentationName)) - .getOrElse(throw new NoSuchElementException(s"No multi transition found with name $internalRepresentationName")) - } - - /** - * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. - * - * @param actionTransitions Seq of reflected transition. - * @return A map from input place name to reflected transitions (where the transitions have as input the place). - */ - private def getIngredientsWithMultipleConsumers(actionTransitions: Seq[InteractionTransition]): Map[String, Seq[InteractionTransition]] = { - // Obtain a list of field name with their transition - val placeNameWithTransition: Seq[(String, InteractionTransition)] = for { - transition <- actionTransitions - inputPlaceName <- transition.nonProvidedIngredients.map(_.name) - } yield (inputPlaceName, transition) - - // Then obtain the places with multiple out-adjacent transitions - val ingredientsWithMultipleConsumers = placeNameWithTransition.groupBy { - case (placeName, _) => placeName - } // Group by place name - .filter { case (_, interactions) => interactions.size >= 2 } // Only keep those place names which have more than one out-adjacent transition - .map { case (placeName, interactions) => (placeName, interactions.map(_._2)) } // Cleanup the data structure - - ingredientsWithMultipleConsumers - } - - private def createPlace(label: String, placeType: PlaceType): Place = Place(label = s"${placeType.labelPrepend}$label", placeType) + def compileRecipe(recipe: Recipe, validationSettings: ValidationSettings): CompiledRecipe = + RecipeCompilerScala.compileRecipe(recipe, validationSettings) } diff --git a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala new file mode 100644 index 000000000..9d4406aac --- /dev/null +++ b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala @@ -0,0 +1,416 @@ +package com.ing.baker.compiler + +import com.ing.baker.il.CompiledRecipe.{OldRecipeIdVariant, Scala212CompatibleJava, Scala212CompatibleKotlin, Scala212CompatibleScala} +import com.ing.baker.il.RecipeValidations.postCompileValidations +import com.ing.baker.il.petrinet.Place._ +import com.ing.baker.il.petrinet._ +import com.ing.baker.il.{CompiledRecipe, EventDescriptor, ValidationSettings, checkpointEventInteractionPrefix, sieveInteractionPrefix, subRecipePrefix} +import com.ing.baker.petrinet.api._ +import com.ing.baker.recipe.common._ +import com.ing.baker.recipe.{javadsl, kotlindsl} +import com.ing.baker.recipe.scaladsl.{Event, Interaction} +import scalax.collection.edge.WLDiEdge +import scalax.collection.immutable.Graph + +import scala.annotation.nowarn +import scala.language.postfixOps + +private object RecipeCompilerScala { + + implicit class TupleSeqOps[A, B](seq: Seq[(Seq[A], Seq[B])]) { + def unzipFlatten: (Seq[A], Seq[B]) = seq.unzip match { + case (a, b) => (a.flatten, b.flatten) + } + } + + @nowarn + def arc(t: Transition, p: Place, weight: Long): Arc = WLDiEdge[Node, Edge](Right(t), Left(p))(weight, Edge(None)) + + @nowarn + def arc(p: Place, t: Transition, weight: Long, eventFilter: Option[String] = None): Arc = { + WLDiEdge[Node, Edge](Left(p), Right(t))(weight, Edge(eventFilter)) + } + + /** + * Creates a transition for a missing event in the recipe. + */ + private def missingEventTransition[E](eventName: String): MissingEventTransition = MissingEventTransition(eventName) + + private def buildEventAndPreconditionArcs(interaction: InteractionDescriptor, + preconditionTransition: String => Option[Transition], + interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { + + //Find the event in available events + + interaction.requiredEvents.toIndexedSeq.map { eventName => + // a new `Place` generated for each AND events + val eventPreconditionPlace = createPlace(label = s"$eventName-${interaction.name}", placeType = EventPreconditionPlace) + + buildEventPreconditionArcs(eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name).get) + }.unzipFlatten + } + + private def buildEventORPreconditionArcs(interaction: InteractionDescriptor, + preconditionTransition: String => Option[Transition], + interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { + + interaction.requiredOneOfEvents.toIndexedSeq.zipWithIndex.map { case (orGroup: Set[String], index: Int) => + // only one `Place` for all the OR events + val eventPreconditionPlace = createPlace(label = s"${interaction.name}-or-$index", placeType = EventOrPreconditionPlace) + + orGroup.toIndexedSeq.map { eventName => + buildEventPreconditionArcs(eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name).get) + }.unzipFlatten + }.unzipFlatten + } + + private def buildEventPreconditionArcs(eventName: String, + preconditionPlace: Place, + preconditionTransition: String => Option[Transition], + interactionTransition: Transition): (Seq[Arc], Seq[String]) = { + + val eventTransition = preconditionTransition(eventName) + + val notProvidedError = eventTransition match { + case None => Seq(s"Event '$eventName' for '$interactionTransition' is not provided in the recipe") + case Some(_) => Seq.empty + } + + val arcs = Seq( + arc(eventTransition.getOrElse(missingEventTransition(eventName)), preconditionPlace, 1), + arc(preconditionPlace, interactionTransition, 1) + ) + + (arcs, notProvidedError) + } + + // the (possible) event output arcs / places + private def buildInteractionOutputArcs(interaction: InteractionTransition, + eventTransitions: Seq[EventTransition]): Seq[Arc] = { + val resultPlace = createPlace(label = interaction.label, placeType = InteractionEventOutputPlace) + if (interaction.eventsToFire.nonEmpty) { + val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor => + //Get the correct event transition + val eventTransition = eventTransitions.find(_.event.name == event.name).get + //Decide if there are multiple interactions that fire this transition, + // if so create a event combiner place + // else link the transition to the event. + val eventTransitionCount = eventTransitions.count(e => e.event.name == event.name) + if (eventTransitionCount > 1) { + //Create a new intermediate event place + val eventCombinerPlace: Place = createPlace(label = event.name, placeType = IntermediatePlace) + //Create a new intermediate event transition + val interactionToEventTransition: IntermediateTransition = IntermediateTransition(s"${interaction.interactionName}:${event.name}") + //link the interaction output place to the intermediate transition + val interactionOutputPlaceToIntermediateTransition: Arc = arc(resultPlace, interactionToEventTransition, 1, Some(event.name)) + //link the intermediate transition to the intermediate input place + val intermediateTransitionToEventCombinerPlace: Arc = arc(interactionToEventTransition, eventCombinerPlace, 1) + //Link the intermediate place to the event place + val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) + Seq(intermediateTransitionToEventCombinerPlace, interactionOutputPlaceToIntermediateTransition, eventCombinerPlaceToEventTransition) + } + else { + val internalEventTransition = eventTransition + Seq(arc(resultPlace, internalEventTransition, 1, Some(event.name))) + } + } + arc(interaction, resultPlace, 1) +: eventArcs + } + else Seq.empty + } + + + /** + * Draws an arc from all required ingredients for an interaction + * If the ingredient has multiple consumers create a multi transition place and create both arcs for it + */ + private def buildInteractionInputArcs(t: InteractionTransition, + multipleConsumerFacilitatorTransitions: Seq[Transition], + ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]]): Seq[Arc] = { + + val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = + t.nonProvidedIngredients.map(_.name).partition(ingredientsWithMultipleConsumers.contains) + + // the extra arcs to model multiple output transitions from one place + val internalDataInputArcs: Seq[Arc] = fieldNamesWithPrefixMulti flatMap { fieldName => + val multiTransitionPlace = createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace) + Seq( + // one arc from multiplier place to the transition + arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), + multiTransitionPlace, + 1), + // one arc from extra added place to transition + arc(multiTransitionPlace, t, 1)) + } + + // the data input arcs / places + val dataInputArcs: Seq[Arc] = fieldNamesWithoutPrefix.map(fieldName => arc(createPlace(fieldName, IngredientPlace), t, 1)) + + val dataOutputArcs: Seq[Arc] = + if (t.isReprovider) + fieldNamesWithoutPrefix.map(fieldName => arc(t, createPlace(fieldName, IngredientPlace), 1)) ++ + fieldNamesWithPrefixMulti.map(fieldName => arc(t, createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace), 1)) + else + Seq.empty + + val limitInteractionCountArc: Option[Arc] = + t.maximumInteractionCount.map(n => arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) + + dataInputArcs ++ dataOutputArcs ++ internalDataInputArcs ++ limitInteractionCountArc + } + + private def buildInteractionArcs(multipleOutputFacilitatorTransitions: Seq[Transition], + placeNameWithDuplicateTransitions: Map[String, Seq[InteractionTransition]], + eventTransitions: Seq[EventTransition]) + (t: InteractionTransition): Seq[Arc] = { + + val inputArcs: Seq[Arc] = buildInteractionInputArcs(t, multipleOutputFacilitatorTransitions, placeNameWithDuplicateTransitions) + + val outputArcs: Seq[Arc] = buildInteractionOutputArcs(t, eventTransitions) + + inputArcs ++ outputArcs + } + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + def compileRecipe(recipe: Recipe, + validationSettings: ValidationSettings): CompiledRecipe = { + + val precompileErrors: Seq[String] = PreCompileValidations.INSTANCE.preCompileAssertions(recipe) + + // Extend the interactions with the checkpoint event interactions and sub-recipes + val actionDescriptors: Seq[InteractionDescriptor] = recipe.interactions ++ + recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ + recipe.sieves.map(convertSieveToInteraction) ++ + recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) + + // Flatten all sensory events from sub recipes + val sensoryEvents = flattenSensoryEvents(recipe) + + //All ingredient names provided by sensory events or by interactions + val allIngredientNames: Set[String] = + sensoryEvents.flatMap(e => e.providedIngredients.map(i => i.name)) ++ + actionDescriptors.flatMap(i => i.output.flatMap { e => + // check if the event was renamed (check if there is a transformer for this event) + i.eventOutputTransformers.get(e) match { + case Some(transformer) => e.providedIngredients.map(ingredient => transformer.ingredientRenames.getOrElse(ingredient.name, ingredient.name)) + case None => e.providedIngredients.map(_.name) + } + } + ) + + // For inputs for which no matching output cannot be found, we do not want to generate a place. + // It should be provided at runtime from outside the active petri net (marking) + val interactionTransitions = actionDescriptors.map(_.toInteractionTransition(recipe.defaultFailureStrategy, allIngredientNames)) + + val allInteractionTransitions: Seq[InteractionTransition] = interactionTransitions + + // events provided from outside + val sensoryEventTransitions: Seq[EventTransition] = sensoryEvents.map { + event => EventTransition(eventToCompiledEvent(event), isSensoryEvent = true, event.maxFiringLimit) + }.toIndexedSeq + + // events provided by other transitions / actions + val interactionEventTransitions: Seq[EventTransition] = allInteractionTransitions.flatMap { t => + t.eventsToFire.map(event => EventTransition(event, isSensoryEvent = false)) + } + + val allEventTransitions: Seq[EventTransition] = sensoryEventTransitions ++ interactionEventTransitions + + // Given the event classes, it is creating the ingredient places and + // connecting a transition to a ingredient place. + val internalEventArcs: Seq[Arc] = allInteractionTransitions.flatMap { t => + t.eventsToFire.flatMap { event => + event.ingredients.map { ingredient => + val from = interactionEventTransitions.find(_.label == event.name).get + arc(from, createPlace(ingredient.name, IngredientPlace), 1) + } + } + } + + //Create event limiter places so that events can only fire x amount of times. + val eventLimiterArcs: Seq[Arc] = sensoryEventTransitions.flatMap( + t => t.maxFiringLimit match { + case Some(n) => Seq(arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) + case None => Seq.empty + } + ) + + def findEventTransitionByEventName(eventName: String) = allEventTransitions.find(_.event.name == eventName) + + def findInteractionByLabel(label: String) = allInteractionTransitions.find(_.label == label) + + // This generates precondition arcs for Required Events (AND). + val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t => + buildEventAndPreconditionArcs(t, + findEventTransitionByEventName, + findInteractionByLabel) + }.unzipFlatten + + // This generates precondition arcs for Required Events (OR). + val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t => + buildEventORPreconditionArcs(t, + findEventTransitionByEventName, + findInteractionByLabel) + }.unzipFlatten + + val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition(_.event.ingredients.isEmpty) + + // It connects a sensory event to an ingredient places + val sensoryEventArcs: Seq[Arc] = sensoryEventWithIngredients + .flatMap(et => et.event.ingredients.map(ingredient => arc(et, createPlace(ingredient.name, IngredientPlace), 1))) + + val eventThatArePreconditions: Seq[String] = + actionDescriptors.flatMap { + actionDescriptor => actionDescriptor.requiredEvents ++ actionDescriptor.requiredOneOfEvents.flatten + } + + // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net + val sensoryEventArcsNoIngredientsArcs: Seq[Arc] = sensoryEventWithoutIngredients + //Filter out events that are preconditions to interactions + .filterNot(sensoryEvent => eventThatArePreconditions.contains(sensoryEvent.label)) + .map(sensoryEvent => arc(sensoryEvent, createPlace(sensoryEvent.label, EmptyEventIngredientPlace), 1)) + + // First find the cases where multiple transitions depend on the same ingredient place + val ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]] = + getIngredientsWithMultipleConsumers(allInteractionTransitions) + + // Add one new transition for each duplicate input (the newly added one in the image above) + val multipleConsumerFacilitatorTransitions: Seq[Transition] = + ingredientsWithMultipleConsumers.keys + .map(name => MultiFacilitatorTransition(label = name)) + .toIndexedSeq + + val multipleOutputFacilitatorArcs: Seq[Arc] = + multipleConsumerFacilitatorTransitions.map(t => + arc(createPlace(t.label, IngredientPlace), t, 1)) + + val interactionArcs: Seq[Arc] = + allInteractionTransitions.flatMap( + buildInteractionArcs( + multipleConsumerFacilitatorTransitions, + ingredientsWithMultipleConsumers, + interactionEventTransitions)) + + val arcs = (interactionArcs + ++ eventPreconditionArcs + ++ eventOrPreconditionArcs + ++ eventLimiterArcs + ++ sensoryEventArcs + ++ sensoryEventArcsNoIngredientsArcs + ++ internalEventArcs + ++ multipleOutputFacilitatorArcs) + + val petriNet: PetriNet = new PetriNet(Graph(arcs: _*)) + + val initialMarking: Marking[Place] = petriNet.places.collect { + case p@Place(_, FiringLimiterPlace(n)) => p -> Map[Any, Int]((null, n)) + }.toMarking + + val errors = preconditionORErrors ++ preconditionANDErrors ++ precompileErrors + + val oldRecipeIdVariant: OldRecipeIdVariant = + recipe match { + case _: javadsl.Recipe => Scala212CompatibleJava + case _: kotlindsl.Recipe => Scala212CompatibleKotlin + case _ => Scala212CompatibleScala + } + + val compiledRecipe = CompiledRecipe.build( + name = recipe.name, + petriNet = petriNet, + initialMarking = initialMarking, + validationErrors = errors, + eventReceivePeriod = recipe.eventReceivePeriod, + retentionPeriod = recipe.retentionPeriod, + oldRecipeIdVariant = oldRecipeIdVariant, + ) + + postCompileValidations(compiledRecipe, validationSettings) + } + + private def getMultiTransition(internalRepresentationName: String, transitions: Seq[Transition]): Transition = + transitions + .find(t => t.label.equals(internalRepresentationName)) + .getOrElse(throw new NoSuchElementException(s"No multi transition found with name $internalRepresentationName")) + + /** + * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. + * + * @param actionTransitions Seq of reflected transition. + * @return A map from input place name to reflected transitions (where the transitions have as input the place). + */ + private def getIngredientsWithMultipleConsumers(actionTransitions: Seq[InteractionTransition]): Map[String, Seq[InteractionTransition]] = { + // Obtain a list of field name with their transition + val placeNameWithTransition: Seq[(String, InteractionTransition)] = for { + transition <- actionTransitions + inputPlaceName <- transition.nonProvidedIngredients.map(_.name) + } yield (inputPlaceName, transition) + + // Then obtain the places with multiple out-adjacent transitions + val ingredientsWithMultipleConsumers = placeNameWithTransition.groupBy { + case (placeName, _) => placeName + } // Group by place name + .filter { case (_, interactions) => interactions.size >= 2 } // Only keep those place names which have more than one out-adjacent transition + .map { case (placeName, interactions) => (placeName, interactions.map(_._2)) } // Cleanup the data structure + + ingredientsWithMultipleConsumers + } + + private def createPlace(label: String, placeType: PlaceType): Place = Place(label = s"${placeType.labelPrepend}$label", placeType) + + private def convertCheckpointEventToInteraction(e: CheckPointEvent) = + Interaction( + name = s"${checkpointEventInteractionPrefix}${e.name}", + inputIngredients = Seq.empty, + output = Seq(Event(e.name)), + requiredEvents = e.requiredEvents, + requiredOneOfEvents = e.requiredOneOfEvents) + + private def convertSieveToInteraction(s: Sieve) = + Interaction( + name = s"${sieveInteractionPrefix}${s.name}", + inputIngredients = s.inputIngredients, + output = s.output, + requiredEvents = Set.empty, + requiredOneOfEvents = Set.empty + ) + + private def flattenSubRecipesToInteraction(recipe: Recipe): Set[InteractionDescriptor] = { + def copyInteraction(i: InteractionDescriptor) = Interaction( + name = s"${subRecipePrefix}${recipe.name}$$${i.name}", + inputIngredients = i.inputIngredients, + output = i.output, + requiredEvents = i.requiredEvents, + requiredOneOfEvents = i.requiredOneOfEvents, + predefinedIngredients = i.predefinedIngredients, + overriddenIngredientNames = i.overriddenIngredientNames, + overriddenOutputIngredientName = i.overriddenOutputIngredientName, + maximumInteractionCount = i.maximumInteractionCount, + failureStrategy = i.failureStrategy, + eventOutputTransformers = i.eventOutputTransformers, + isReprovider = i.isReprovider, + oldName = Option(i.originalName) + ) + + recipe.interactions.map(copyInteraction).toSet ++ + recipe.sieves.map(convertSieveToInteraction) ++ + recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ + recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) + } + + private def flattenSensoryEvents(recipe: Recipe): Set[com.ing.baker.recipe.common.Event] = { + recipe.sensoryEvents ++ recipe.subRecipes.flatMap(flattenSensoryEvents) + } +} diff --git a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeValidationException.scala b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeValidationException.scala deleted file mode 100644 index 2651cf636..000000000 --- a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeValidationException.scala +++ /dev/null @@ -1,3 +0,0 @@ -package com.ing.baker.compiler - -class RecipeValidationException(reason: String) extends RuntimeException(reason) diff --git a/core/baker-compiler/src/test/java/RecipeCompilerTests.java b/core/baker-compiler/src/test/java/com/ing/baker/compiler/RecipeCompilerTests.java similarity index 97% rename from core/baker-compiler/src/test/java/RecipeCompilerTests.java rename to core/baker-compiler/src/test/java/com/ing/baker/compiler/RecipeCompilerTests.java index 6be0d2494..5c453adc5 100644 --- a/core/baker-compiler/src/test/java/RecipeCompilerTests.java +++ b/core/baker-compiler/src/test/java/com/ing/baker/compiler/RecipeCompilerTests.java @@ -1,4 +1,5 @@ -import com.ing.baker.compiler.RecipeCompiler; +package com.ing.baker.compiler; + import com.ing.baker.il.CompiledRecipe; import com.ing.baker.il.EventDescriptor; import com.ing.baker.il.petrinet.InteractionTransition; @@ -131,10 +132,10 @@ public void shouldFailOnDuplicatedEventsEvenWithFiringLimitDifference() { .withSensoryEvents(EventA.class, EventB.class, EventC.class, EventD.class, EventE.class) .withSensoryEventsNoFiringLimit(EventA.class); - Assertions.assertThrows(IllegalStateException.class, () -> + Assertions.assertThrows(RecipeValidationException.class, () -> RecipeCompiler.compileRecipe(recipe1)); - Assertions.assertThrows(IllegalStateException.class, () -> + Assertions.assertThrows(RecipeValidationException.class, () -> RecipeCompiler.compileRecipe(recipe2)); } diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerSpecTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerSpecTest.kt new file mode 100644 index 000000000..c6b549e3b --- /dev/null +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerSpecTest.kt @@ -0,0 +1,938 @@ +package com.ing.baker.compiler + +import com.ing.baker.compiler.ScalaConversions.asJava +import com.ing.baker.compiler.ScalaConversions.asScala +import com.ing.baker.il.CompiledRecipe +import com.ing.baker.il.EventDescriptor +import com.ing.baker.il.IngredientDescriptor +import com.ing.baker.il.ValidationSettings +import com.ing.baker.il.petrinet.InteractionTransition +import com.ing.baker.recipe.TestRecipe.eventFromInteractionTwo +import com.ing.baker.recipe.TestRecipe.exhaustedEvent +import com.ing.baker.recipe.TestRecipe.getRecipe +import com.ing.baker.recipe.TestRecipe.initialEvent +import com.ing.baker.recipe.TestRecipe.initialEventExtendedName +import com.ing.baker.recipe.TestRecipe.initialIngredient +import com.ing.baker.recipe.TestRecipe.interactionEight +import com.ing.baker.recipe.TestRecipe.interactionFive +import com.ing.baker.recipe.TestRecipe.interactionFour +import com.ing.baker.recipe.TestRecipe.interactionOne +import com.ing.baker.recipe.TestRecipe.interactionOneSuccessful +import com.ing.baker.recipe.TestRecipe.interactionSeven +import com.ing.baker.recipe.TestRecipe.interactionSix +import com.ing.baker.recipe.TestRecipe.interactionThree +import com.ing.baker.recipe.TestRecipe.interactionTwo +import com.ing.baker.recipe.TestRecipe.interactionWithOptionalIngredients +import com.ing.baker.recipe.TestRecipe.missingJavaOptional +import com.ing.baker.recipe.TestRecipe.missingJavaOptionalDirectString +import com.ing.baker.recipe.TestRecipe.notUsedSensoryEvent +import com.ing.baker.recipe.TestRecipe.providesNothingInteraction +import com.ing.baker.recipe.TestRecipe.secondEvent +import com.ing.baker.recipe.TestRecipe.sieveInteraction +import com.ing.baker.recipe.TestRecipeJava +import com.ing.baker.recipe.common.EventOutputTransformer +import com.ing.baker.recipe.common.Ingredient +import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.common.`package$` +import com.ing.baker.recipe.scaladsl.CheckPointEvent +import com.ing.baker.recipe.scaladsl.Event +import com.ing.baker.recipe.scaladsl.Interaction +import com.ing.baker.recipe.scaladsl.`Interaction$` +import com.ing.baker.recipe.scaladsl.Recipe +import com.ing.baker.types.Converters +import com.ing.baker.types.`Int32$` +import com.ing.baker.types.`NullValue$` +import com.ing.baker.types.PrimitiveValue +import com.ing.baker.types.RecordField +import com.ing.baker.types.RecordType +import com.ing.baker.types.Value +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail +import scala.Option +import scala.Some +import scala.Tuple2 +import scala.collection.immutable.Seq +import scala.concurrent.duration.Duration +import java.util.Optional +import java.util.concurrent.TimeUnit +import kotlin.reflect.javaType +import kotlin.reflect.typeOf + +class RecipeCompilerScalaTest { + + @Test + fun `The recipe compiler should not have validation errors for a valid recipe`() { + val recipe: Recipe = getRecipe("ValidRecipe") + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(compiledRecipe.validationErrors.isEmpty()) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with FireEventAfterFailure (with name)`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAfterFailure(Some(exhaustedEvent().name())) + ) + ) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with FireEventAfterFailure (no name)`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAfterFailure(Option.empty()) + ) + ) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with FireEventAndBlock (with name)`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndBlock(Some(exhaustedEvent().name())) + ) + ) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with FireEventAndBlock (no name)`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndBlock(Option.empty()) + ) + ) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with FireEventAndResolve (with name)`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndResolve(Some(exhaustedEvent().name())) + ) + ) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with FireEventAndResolve (no name)`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndResolve(Option.empty()) + ) + ) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("InteractionOneFunctionalFailed" in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireRetryExhaustedEvent(exhaustedEvent()) + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent 2`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireRetryExhaustedEvent(Some("RetryExhausted")) + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent 3`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireRetryExhaustedEvent() + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireRetryExhaustedEvent(exhaustedEvent()) + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock 2`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireRetryExhaustedEvent(Some("RetryExhausted")) + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock 3`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireEventAndBlock() + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireEventAndResolve(exhaustedEvent()) + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve 2`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireEventAndResolve(Some("RetryExhausted")) + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve32`() { + val recipe = recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent()) + .withInteractions( + interactionOne().withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilDeadline(10.seconds))) + .withFireEventAndResolve() + .build() + ) + ) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue("InteractionOneFunctionalFailed" in compiledRecipe.allEvents.map(EventDescriptor::name)) + } + + @Test + fun `The recipe compiler should generate the same id for the same recipe`() { + val first = RecipeCompiler.compileRecipe(getRecipe("ValidRecipe")).recipeId() + (1..10) + .map { getRecipe("ValidRecipe") } + .map { RecipeCompiler.compileRecipe(it).recipeId() } + .forEach { assertEquals(first, it) } + } + + @Test + fun `The recipe compiler should generate different ids for recipes with changes on transitions other than the name`() { + val input = ingredient("ingredient") + val output = event("event", emptyList()) + val interaction = interaction("interaction", listOf(input), listOf(output)) + val name = "RecipeName" + val recipe1 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 1)) + val recipe2 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 2)) + assertTrue(RecipeCompiler.compileRecipe(recipe1).recipeId() != RecipeCompiler.compileRecipe(recipe2).recipeId()) + } + + @Test + fun `The recipe compiler should give a list of missing ingredients if an interaction has an ingredient that is not provided by any other event or interaction`() { + val recipe = recipe("NonProvidedIngredient") + .withSensoryEvent(secondEvent()) + .withInteractions(interactionOne()) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give an error if the RecipeInstanceId is required and is not of the String type`() { + val wrongrecipeInstanceIdInteraction = + interaction( + name = "wrongrecipeInstanceIdInteraction", + inputIngredients = listOf(ingredient(common.recipeInstanceIdName), initialIngredient()), + output = emptyList() + ) + + val recipe = recipe("NonProvidedIngredient") + .withSensoryEvent(initialEvent()) + .withInteractions(wrongrecipeInstanceIdInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Non supported process id type: Int32 on interaction: 'wrongrecipeInstanceIdInteraction'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give an error if the MetaData is required and is not of the Map String to String type`() { + val wrongMetaDataInteraction = + interaction( + name = "wrongMetaDataInteraction", + inputIngredients = listOf(ingredient(common.recipeInstanceMetadataName), initialIngredient()), + output = emptyList() + ) + + val recipe = recipe("NonProvidedIngredient") + .withSensoryEvent(initialEvent()) + .withInteractions(wrongMetaDataInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Non supported MetaData type: Int32 on interaction: 'wrongMetaDataInteraction'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give an error if the baker internal ingredients are provided`() { + val wrongDateEvent = event( + "WrongDataEvent", + listOf( + ingredient("recipeInstanceId"), + ingredient("RecipeInstanceMetaData") + ), + maxFiringLimit = null + ) + + val wrongDateEvent2 = event( + "WrongDataEvent2", + listOf(ingredient("RecipeInstanceEventList")), + maxFiringLimit = null + ) + + val wrongMetaDataInteraction = + interaction( + name = "wrongDataProvidedInteraction", + inputIngredients = listOf(ingredient(common.recipeInstanceIdName), initialIngredient()), + output = listOf(wrongDateEvent2) + ) + + val recipe = recipe("WrongDataRecipe") + .withSensoryEvents(initialEvent(), wrongDateEvent) + .withInteractions(wrongMetaDataInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals( + listOf( + "Ingredient 'RecipeInstanceMetaData' is provided and this is a reserved name for internal use in Baker", + "Ingredient 'recipeInstanceId' is provided and this is a reserved name for internal use in Baker", + "Ingredient 'RecipeInstanceEventList' is provided and this is a reserved name for internal use in Baker" + ).sorted(), + compiledRecipe.validationErrors.sorted() + ) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients when an ingredient is of the wrong type`() { + val initialIngredientInt = Ingredient( + "initialIngredient", recordType( + listOf( + RecordField("data", Int32) + ) + ) + ) + val initialEventInt = event("InitialEvent", listOf(initialIngredientInt)) + + val recipe = recipe("WrongTypedIngredient") + .withInteractions( + interactionOne() + ) + .withSensoryEvent(initialEventInt) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Interaction 'InteractionOne' expects ingredient 'initialIngredient:CharArray', however incompatible type: 'Record(data: Int32)' was provided" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients when an optional ingredient is of the wrong Option type`() { + val initialIngredientOptionalInt = ingredient>("initialIngredientOptionalInt") + val initialIngredientOptionalString = ingredient>("initialIngredientOptionalInt") + val initialIngredientOptionInt = ingredient>>("initialIngredientOptionInt") + val initialIngredientOptionString = ingredient>>("initialIngredientOptionInt") + val initialEventIntOptional = event("initialEventIntOptional", listOf(initialIngredientOptionalString)) + val initialEventIntOption = event("initialEventIntOption", listOf(initialIngredientOptionString)) + val interactionOptional = + interaction( + name = "InteractionWithOptional", + inputIngredients = listOf(recipeInstanceId, initialIngredientOptionalInt, initialIngredientOptionInt), + output = emptyList() + ) + + val recipe = recipe("WrongTypedOptionalIngredient") + .withInteractions( + interactionOptional + ) + .withSensoryEvents(initialEventIntOptional, initialEventIntOption) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionalInt:Option[Int32]', however incompatible type: 'Option[CharArray]' was provided" in compiledRecipe.validationErrors) + assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionInt:Option[List[Int32]]', however incompatible type: 'Option[List[CharArray]]' was provided" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give no errors if an Optional ingredient is of the correct Option type`() { + val initialIngredientInt = ingredient>>("initialIngredient") + val initialEventInt = event("InitialEvent", listOf(initialIngredientInt)) + val interactionOptional = + interaction( + name = "InteractionWithOptional", + inputIngredients = listOf(recipeInstanceId, initialIngredientInt), + output = emptyList() + ) + + val recipe = recipe("CorrectTypedOptionalIngredient") + .withInteractions( + interactionOptional + ) + .withSensoryEvent(initialEventInt) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(compiledRecipe.validationErrors.isEmpty()) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients if a predefined ingredient is of the wrong type`() { + val recipe = recipe("WrongGivenPredefinedIngredient") + .withInteractions( + interactionOne() + .withRequiredEvent(initialEvent()) + .withPredefinedIngredients("initialIngredient" to Integer.valueOf(12)) + ) + .withSensoryEvent(initialEvent()) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Predefined argument 'initialIngredient' is not of type: CharArray on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should give a list of wrong ingredients if a predefined ingredient is not needed by the interaction`() { + val recipe = recipe("WrongGivenIngredient") + .withInteractions( + interactionOne() + .withPredefinedIngredients("WrongIngredient" to null) + ) + .withSensoryEvent(initialEvent()) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue("Predefined argument 'WrongIngredient' is not defined on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should validate if unreachable interactions exist or not`() { + val recipe = recipe("RecipeWithUnreachableInteraction") + .withInteractions(interactionSeven().withMaximumInteractionCount(1), interactionEight()) + .withSensoryEvent(initialEvent()) + + val compiledRecipe = RecipeCompiler.compileRecipe( + recipe, + ValidationSettings(true, true , false) + ) + + assertTrue("InteractionEight is not executable" in compiledRecipe.validationErrors) + } + + @Test + fun `The recipe compiler should fail compilation when the interaction name is null or empty`() { + listOf("", null).forEach { name -> + val invalidInteraction = interaction(name, emptyList(), emptyList()) + val recipe = + recipe("InteractionNameTest").withInteractions(invalidInteraction).withSensoryEvent(initialEvent()) + + assertFailsWith(exceptionMessage = "Interaction with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + fun `The recipe compiler should fail compilation when the event name is null or empty`() { + listOf("", null).forEach { name -> + val invalidEvent = event(name) + val recipe = recipe("EventNameTest").withSensoryEvent(invalidEvent).withInteraction(interactionOne()) + + assertFailsWith(exceptionMessage = "Event with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + fun `The recipe compiler should fail compilation when the ingredient name is null or empty`() { + listOf("", null).forEach { name -> + val invalidIngredient = ingredient(name) + val recipe = recipe("IngredientNameTest").withSensoryEvent(event("someEvent", listOf(invalidIngredient))) + .withInteraction(interactionOne()) + + assertFailsWith(exceptionMessage = "Ingredient with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + fun `The recipe compiler should fail compilation when the recipe name is null or empty`() { + listOf("", null).forEach { name -> + val recipe = recipe(name) + + assertFailsWith(exceptionMessage = "Recipe with a null or empty name found") { + RecipeCompilerKotlin.compileRecipe(recipe) + } + } + } + + @Test + fun `The recipe compiler should report an error when an Interaction is reprovider, but has no required events`() { + val recipe: Recipe = recipe("LoopingWithReprovider") + .withInteraction(interactionOne().isReprovider(true)) + .withSensoryEvents(initialEvent()) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals( + listOf("Reprovider interaction InteractionOne needs to have a event precondition"), + compiledRecipe.validationErrors + ) + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient as empty when the ingredient is not provided`() { + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients()) + .withSensoryEvent(initialEvent()) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(4, transition.predefinedParameters().size()) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is provided`() { + val optionalProviderEvent = event("optionalProviderEvent", listOf(missingJavaOptional())) + + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients()) + .withSensoryEvents(initialEvent(), optionalProviderEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(3, transition.predefinedParameters().size()) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is renamed and provided via an event`() { + val stringOptionIngredient = ingredient>("stringOptionIngredient") + val renamedStringOptionIngredient = ingredient>("renamedStringOptionIngredient") + + val eventWithOptionIngredient = event("eventWithOptionIngredient", listOf(stringOptionIngredient)) + + val interactionWithOptionIngredient = + interaction("interactionWithOptionIngredient", listOf(initialIngredient()), listOf(eventWithOptionIngredient)) + + val secondInteraction = interaction("secondInteraction", listOf(renamedStringOptionIngredient), emptyList()) + + val recipe = recipe("interactionWithEventOutputTransformer") + .withSensoryEvent(initialEvent()) + .withInteraction( + interactionWithOptionIngredient + .withEventOutputTransformer( + eventWithOptionIngredient, + "RenamedEventWithOptionIngredient", + mapOf("stringOptionIngredient" to "renamedStringOptionIngredient").asScala + ) + ) + .withInteraction(secondInteraction) + + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertTrue(compiledRecipe.validationErrors.isEmpty()) + + val transition = + compiledRecipe.interactionTransitions().find { it.interactionName() == "secondInteraction" }.get() + assertTrue( + "renamedStringOptionIngredient" in transition.nonProvidedIngredients().asJava.map( + IngredientDescriptor::name + ) + ) + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is predefined`() { + val ingredientValue = Optional.of("value") + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction( + interactionWithOptionalIngredients() + .withPredefinedIngredients("missingJavaOptional" to ingredientValue) + ) + .withSensoryEvents(initialEvent()) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(4, transition.predefinedParameters().size()) + assertEquals(Some(PrimitiveValue("value")), transition.predefinedParameters()["missingJavaOptional"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is provided, but not wrapped in an Option type`() { + val optionalProviderEvent = event("optionalProviderEvent", listOf(missingJavaOptionalDirectString())) + + val recipe: Recipe = recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients()) + .withSensoryEvents(initialEvent(), optionalProviderEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + assertTrue(compiledRecipe.validationErrors.isEmpty()) + compiledRecipe.interactionTransitions().foreach { transition -> + if (transition.interactionName().equals("OptionalIngredientInteraction")) { + assertEquals(3, transition.predefinedParameters().size()) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) + assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) + } + } + } + + @Test + fun `The recipe compiler should give the correct id when it compiles a java recipe`() { + val recipe = TestRecipeJava.getRecipe("id-test-recipe") + val compiledRecipeKotlin = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipeScala = RecipeCompiler.compileRecipe(recipe) + assertEquals(compiledRecipeScala.recipeId(), compiledRecipeKotlin.recipeId()) + + assertEquals("220827c42a75b3f8", compiledRecipeKotlin.recipeId()) + } + + @Test + fun `The recipe compiler should give the interaction with Reprovider enabled when it compiles a java recipe and changes recipeId`() { + val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals("416e8abc02abcbee", compiledRecipe.recipeId()) + } + + @Test + fun `The recipe compiler should give the interaction for checkpoint-events when it compiles a java recipe`() { + val recipe = TestRecipeJava.getRecipe("id-test-recipe") + .withCheckpointEvent( + CheckPointEvent.apply( + "Success", + emptySet().asScala, + emptySet>().asScala + ).withRequiredEvent(initialEvent()) + ) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + assertEquals("469441173f91869a", compiledRecipe.recipeId()) + assertEquals(1, compiledRecipe.petriNet().transitions().count { + it is InteractionTransition && it.interactionName() + .contains("${checkpointEventInteractionPrefix}Success") + }) + } + + @Test + fun `give the interaction for sub-recipes when it compiles a java recipe`() { + + val subSubRecipe: Recipe = recipe("SubSubRecipe") + .withInteractions( + interactionOne() + .withEventOutputTransformer( + interactionOneSuccessful(), + mapOf("interactionOneOriginalIngredient" to "interactionOneIngredient").asScala + ) + .withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10.milliseconds) + .withUntil(Some(UntilMaximumRetries(3))) + .build() + ), + interactionTwo() + .withOverriddenIngredientName("initialIngredientOld", "initialIngredient"), + ) + + val subRecipe: Recipe = recipe("SubRecipe") + .withInteractions( + interactionThree() + .withMaximumInteractionCount(1), + interactionFour() + .withRequiredEvents(secondEvent(), eventFromInteractionTwo()), + interactionFive(), + interactionSix(), + ) + .withSubRecipe(subSubRecipe) + + val recipe: Recipe = recipe("Recipe") + .withSensoryEvents( + initialEvent(), + initialEventExtendedName(), + secondEvent(), + notUsedSensoryEvent() + ) + .withInteractions( + providesNothingInteraction(), + sieveInteraction() + ) + .withSubRecipe(subRecipe) + + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + + val res = compiledRecipe.petriNet().transitions().asJava + .flatMap { + when (it) { + is InteractionTransition -> listOf(it.interactionName()) + else -> emptyList() + } + } + .filter { it.startsWith(subRecipePrefix) }.toSet() + + assertEquals("ae2282f55f0a4f9f", compiledRecipe.recipeId()) + assertEquals( + setOf( + $$"$SubRecipe$SubSubRecipe$InteractionOne", + $$"$SubRecipe$SubSubRecipe$InteractionTwo", + $$"$SubRecipe$SubRecipe$InteractionThree", + $$"$SubRecipe$SubRecipe$InteractionFour", + $$"$SubRecipe$SubRecipe$InteractionFive", + $$"$SubRecipe$SubRecipe$InteractionSix", + ), res + ) + } + + private inline fun assertFailsWith( + exceptionMessage: String, + message: String? = null, + init: () -> Any? + ) { + try { + init() + } catch (t: Throwable) { + if (t is T) { + if (t.message == exceptionMessage) + return + else + fail(message ?: "Exception ${T::class.simpleName} thrown but message is different") + } + fail(message ?: "Exception thrown but not ${T::class.simpleName}") + } + fail(message ?: "No exception thrown") + } + + private fun recordType(recordsFields: List) = RecordType(recordsFields.asScala) + + private fun recipe(name: String?): Recipe = Recipe.apply(name) + private fun event(name: String?, ingredients: List = emptyList(), maxFiringLimit: Int? = null) = + Event.apply(name, ingredients.asScala).let { + when (maxFiringLimit) { + null -> it + else -> it.withMaxFiringLimit(maxFiringLimit) + } + } + + val recipeInstanceId = ingredient(common.recipeInstanceIdName) + + fun Recipe.withInteractions(vararg newInteractions: InteractionDescriptor) = + this.withInteractions(newInteractions.asScala as Seq?) + + fun Recipe.withSensoryEvents(vararg sensoryEvent: com.ing.baker.recipe.common.Event) = + this.withSensoryEvents(sensoryEvent.asScala as Seq) + + val Int.milliseconds get() = Duration.create(this.toLong(), TimeUnit.MILLISECONDS) + val Int.seconds get() = Duration.create(this.toLong(), TimeUnit.SECONDS) + + object common { + val recipeInstanceIdName = `package$`.`MODULE$`.recipeInstanceIdName() + val recipeInstanceMetadataName = `package$`.`MODULE$`.recipeInstanceMetadataName() + } + + @OptIn(ExperimentalStdlibApi::class) + inline fun ingredient(name: String?) = Ingredient(name, Converters.readJavaType(typeOf().javaType)) + + private fun interaction( + name: String?, + inputIngredients: List, + output: List, + requiredEvents: Set = emptySet(), + requiredOneOfEvents: Set> = emptySet(), + predefinedIngredients: Map = emptyMap(), + overriddenIngredientNames: Map = emptyMap(), + overriddenOutputIngredientName: String? = null, + maximumInteractionCount: Int? = null, + failureStrategy: com.ing.baker.recipe.common.InteractionFailureStrategy? = null, + eventOutputTransformers: Map = emptyMap(), + isReprovider: Boolean = false, + oldName: String? = null, + ): Interaction = + `Interaction$`.`MODULE$`.apply( + name, + inputIngredients.asScala, + output.asScala, + requiredEvents.asScala, + requiredOneOfEvents.map { it: Set -> it.asScala }.toSet().asScala, + predefinedIngredients.asScala, + overriddenIngredientNames.asScala, + Option.apply(overriddenOutputIngredientName), + Option.apply(maximumInteractionCount), + Option.apply(failureStrategy), + eventOutputTransformers.asScala, + isReprovider, + Option.apply(oldName) + ) + + // Ugly Scala glue + val Int32 = `Int32$`.`MODULE$` + val NullValue = `NullValue$`.`MODULE$` + val checkpointEventInteractionPrefix = com.ing.baker.il.`package$`.`MODULE$`.checkpointEventInteractionPrefix() + val subRecipePrefix = com.ing.baker.il.`package$`.`MODULE$`.subRecipePrefix() + + fun Interaction.withPredefinedIngredients(vararg ingredients: Pair) = + this.withPredefinedIngredients(ingredients.map { + Tuple2( + it.first, + it.second + ) + }.asScala) + + fun Interaction.withRequiredEvents(vararg events: com.ing.baker.recipe.common.Event) = + this.withRequiredEvents(events.toList().asScala) + + object InteractionFailureStrategy { + object RetryWithIncrementalBackoff { + fun builder() = + com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$builder$`.`MODULE$`.apply() + } + } + + object UntilDeadline { + operator fun invoke(duration: Duration) = + com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$UntilDeadline$`.`MODULE$`.apply( + duration + ) + + } + + object UntilMaximumRetries { + operator fun invoke(maxRetries: Int) = + com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$UntilMaximumRetries$`.`MODULE$`.apply( + maxRetries + ) + } +} diff --git a/core/baker-compiler/src/test/kotlin/RecipeCompilerTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerTest.kt similarity index 92% rename from core/baker-compiler/src/test/kotlin/RecipeCompilerTest.kt rename to core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerTest.kt index f608bec7c..895edf875 100644 --- a/core/baker-compiler/src/test/kotlin/RecipeCompilerTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerTest.kt @@ -2,8 +2,8 @@ package com.ing.baker.compiler import com.ing.baker.recipe.javadsl.Interaction import com.ing.baker.recipe.kotlindsl.recipe -import org.junit.Assert.assertEquals -import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class RecipeCompilerTest { @@ -44,7 +44,7 @@ class RecipeCompilerTest { event() } interaction() - ingredient("extract") @JvmSerializableLambda {"Hello123"} + ingredient("extract") @JvmSerializableLambda { "Hello123" } } val compiled = RecipeCompiler.compileRecipe(recipe) diff --git a/core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala b/core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala deleted file mode 100644 index ff157948d..000000000 --- a/core/baker-compiler/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala +++ /dev/null @@ -1,637 +0,0 @@ -package com.ing.baker.compiler - -import com.ing.baker.il.petrinet.InteractionTransition - -import java.util.Optional -import com.ing.baker.il.{CompiledRecipe, RecipeVisualStyle, RecipeVisualizer, ValidationSettings, checkpointEventInteractionPrefix, subRecipePrefix} -import com.ing.baker.recipe.TestRecipe._ -import com.ing.baker.recipe.{TestRecipeJava, common} -import com.ing.baker.recipe.common.InteractionFailureStrategy -import com.ing.baker.recipe.common.InteractionFailureStrategy.RetryWithIncrementalBackoff.UntilDeadline -import com.ing.baker.recipe.scaladsl._ -import com.ing.baker.types._ -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpecLike - -import scala.concurrent.duration._ -import scala.language.postfixOps - -class RecipeCompilerSpec extends AnyWordSpecLike with Matchers { - - "The recipe compiler" should { - - "not have validation errors for a valid recipe" in { - val recipe: Recipe = getRecipe("ValidRecipe") - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe List.empty - - // dumpToFile("TestRecipe.svg", compiledRecipe.getVisualRecipeAsSVG) - } - - "add the exhausted retry event to the interaction event output list if defined with FireEventAfterFailure (with name)" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.FireEventAfterFailure(Some(exhaustedEvent.name)))) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with FireEventAfterFailure (no name)" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.FireEventAfterFailure(None))) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") - } - - "add the exhausted retry event to the interaction event output list if defined with FireEventAndBlock (with name)" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.FireEventAndBlock(Some(exhaustedEvent.name)))) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with FireEventAndBlock (no name)" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.FireEventAndBlock(None))) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") - } - - "add the exhausted retry event to the interaction event output list if defined with FireEventAndResolve (with name)" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.FireEventAndResolve(Some(exhaustedEvent.name)))) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with FireEventAndResolve (no name)" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.FireEventAndResolve(None))) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.allEvents.map(_.name) should contain("InteractionOneFunctionalFailed") - } - - "add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireRetryExhaustedEvent(exhaustedEvent) - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent 2" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireRetryExhaustedEvent(Some("RetryExhausted")) - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent 3" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireRetryExhaustedEvent() - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") - } - - "add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock" in { - val exhaustedEvent = Event("RetryExhausted") - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireEventAndBlock(exhaustedEvent) - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock 2" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireEventAndBlock(Some("RetryExhausted")) - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock 3" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireEventAndBlock() - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") - } - - "add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve" in { - val exhaustedEvent = Event("RetryExhausted") - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireEventAndResolve(exhaustedEvent) - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve 2" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireEventAndResolve(Some("RetryExhausted")) - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) - } - - "add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve 3" in { - val recipe = Recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10 milliseconds) - .withUntil(Some(UntilDeadline(10 seconds))) - .withFireEventAndResolve() - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - - compiledRecipe.allEvents.map(_.name) should contain("InteractionOneFunctionalFailed") - } - - "generate the same id for the same recipe" in { - val first = RecipeCompiler.compileRecipe(getRecipe("ValidRecipe")).recipeId - (1 to 10) - .map(_ => getRecipe("ValidRecipe")) - .map(RecipeCompiler.compileRecipe(_).recipeId) - .foreach(_ shouldBe first) - } - - "generate different ids for recipes with changes on transitions other than the name" in { - val input = Ingredient[Int]("ingredient") - val output = Event("event", Seq.empty, None) - val interaction = Interaction("interaction", Seq(input), Seq(output)) - val name = "RecipeName" - val recipe1 = Recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name -> 1)) - val recipe2 = Recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name -> 2)) - RecipeCompiler.compileRecipe(recipe1).recipeId shouldNot be(RecipeCompiler.compileRecipe(recipe2).recipeId) - } - - "give a list of missing ingredients if an interaction has an ingredient that is not provided by any other event or interaction" in { - val recipe = Recipe("NonProvidedIngredient") - .withSensoryEvent(secondEvent) - .withInteractions(interactionOne) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors should contain("Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction") - } - - "give an error if the RecipeInstanceId is required and is not of the String type" in { - val wrongrecipeInstanceIdInteraction = - Interaction( - name = "wrongrecipeInstanceIdInteraction", - inputIngredients = Seq(new Ingredient[Int](common.recipeInstanceIdName), initialIngredient), - output = Seq.empty) - - val recipe = Recipe("NonProvidedIngredient") - .withSensoryEvent(initialEvent) - .withInteractions(wrongrecipeInstanceIdInteraction) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors should contain("Non supported process id type: Int32 on interaction: 'wrongrecipeInstanceIdInteraction'") - } - - "give an error if the MetaData is required and is not of the Map type" in { - val wrongMetaDataInteraction = - Interaction( - name = "wrongMetaDataInteraction", - inputIngredients = Seq(new Ingredient[Int](common.recipeInstanceMetadataName), initialIngredient), - output = Seq.empty) - - val recipe = Recipe("NonProvidedIngredient") - .withSensoryEvent(initialEvent) - .withInteractions(wrongMetaDataInteraction) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors should contain("Non supported MetaData type: Int32 on interaction: 'wrongMetaDataInteraction'") - } - - "give an error if the baker internal ingredients are provided" in { - val wrongDateEvent = Event("WrongDataEvent", - Seq( - Ingredient[String]("recipeInstanceId"), - Ingredient[String]("RecipeInstanceMetaData")), - maxFiringLimit = None) - - val wrongDateEvent2 = Event("WrongDataEvent2", - Seq(Ingredient[String]("RecipeInstanceEventList")), - maxFiringLimit = None) - - val wrongMetaDataInteraction = - Interaction( - name = "wrongDataProvidedInteraction", - inputIngredients = Seq(new Ingredient[String](common.recipeInstanceIdName), initialIngredient), - output = Seq(wrongDateEvent2)) - - val recipe = Recipe("WrongDataRecipe") - .withSensoryEvents(initialEvent, wrongDateEvent) - .withInteractions(wrongMetaDataInteraction) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe List( - "Ingredient 'recipeInstanceId' is provided and this is a reserved name for internal use in Baker", - "Ingredient 'RecipeInstanceMetaData' is provided and this is a reserved name for internal use in Baker", - "Ingredient 'RecipeInstanceEventList' is provided and this is a reserved name for internal use in Baker" - ) - } - - "give a list of wrong ingredients" when { - "an ingredient is of the wrong type" in { - val initialIngredientInt = new common.Ingredient("initialIngredient", RecordType(Seq(RecordField("data", Int32)))) - val initialEventInt = Event("InitialEvent", Seq(initialIngredientInt), None) - - val recipe = Recipe("WrongTypedIngredient") - .withInteractions( - interactionOne) - .withSensoryEvent(initialEventInt) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors should contain("Interaction 'InteractionOne' expects ingredient 'initialIngredient:CharArray', however incompatible type: 'Record(data: Int32)' was provided") - } - - "an optional ingredient is of the wrong Option type" in { - val initialIngredientOptionalInt = Ingredient[Optional[Int]]("initialIngredientOptionalInt") - val initialIngredientOptionalString = Ingredient[Optional[String]]("initialIngredientOptionalInt") - val initialIngredientOptionInt = Ingredient[Option[List[Int]]]("initialIngredientOptionInt") - val initialIngredientOptionString = Ingredient[Option[List[String]]]("initialIngredientOptionInt") - val initialEventIntOptional = Event("initialEventIntOptional", Seq(initialIngredientOptionalString), None) - val initialEventIntOption = Event("initialEventIntOption", Seq(initialIngredientOptionString), None) - val interactionOptional = - Interaction( - name = "InteractionWithOptional", - inputIngredients = Seq(recipeInstanceId, initialIngredientOptionalInt, initialIngredientOptionInt), - output = Seq.empty) - - val recipe = Recipe("WrongTypedOptionalIngredient") - .withInteractions( - interactionOptional) - .withSensoryEvents(initialEventIntOptional, initialEventIntOption) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors should contain("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionalInt:Option[Int32]', however incompatible type: 'Option[CharArray]' was provided") - compiledRecipe.validationErrors should contain("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionInt:Option[List[Int32]]', however incompatible type: 'Option[List[CharArray]]' was provided") - } - } - - "give no errors if an Optional ingredient is of the correct Option type" in { - val initialIngredientInt = Ingredient[Optional[List[Int]]]("initialIngredient") - val initialEventInt = Event("InitialEvent", Seq(initialIngredientInt), None) - val interactionOptional = - Interaction( - name = "InteractionWithOptional", - inputIngredients = Seq(recipeInstanceId, initialIngredientInt), - output = Seq.empty) - - val recipe = Recipe("CorrectTypedOptionalIngredient") - .withInteractions( - interactionOptional) - .withSensoryEvent(initialEventInt) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe empty - } - - "give a list of wrong ingredients if a predefined ingredient is of the wrong type" in { - val recipe = Recipe("WrongGivenPredefinedIngredient") - .withInteractions( - interactionOne - .withRequiredEvent(initialEvent) - .withPredefinedIngredients(("initialIngredient", Integer.valueOf(12)))) - .withSensoryEvent(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors should contain("Predefined argument 'initialIngredient' is not of type: CharArray on interaction: 'InteractionOne'") - } - - "give a list of wrong ingredients if a predefined ingredient is not needed by the interaction" in { - val recipe = Recipe("WrongGivenIngredient") - .withInteractions( - interactionOne - .withPredefinedIngredients(("WrongIngredient", null))) - .withSensoryEvent(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors should contain("Predefined argument 'WrongIngredient' is not defined on interaction: 'InteractionOne'") - } - - "validate if unreachable interactions exist or not" in { - val recipe = Recipe("RecipeWithUnreachableInteraction") - .withInteractions(interactionSeven.withMaximumInteractionCount(1), interactionEight) - .withSensoryEvent(initialEvent) - - val compiledRecipe = RecipeCompiler.compileRecipe(recipe, - ValidationSettings(allowNonExecutableInteractions = false)) - - compiledRecipe.validationErrors should contain("InteractionEight is not executable") - } - - "fail compilation" when { - "the interaction name is null or empty" in { - List("", null) foreach { name => - val invalidInteraction = Interaction(name, Seq.empty, Seq()) - val recipe = Recipe("InteractionNameTest").withInteractions(invalidInteraction).withSensoryEvent(initialEvent) - - intercept[IllegalArgumentException](RecipeCompiler.compileRecipe(recipe)).getMessage.shouldBe("Interaction with a null or empty name found") - } - } - - - "the event name is null or empty" in { - List("", null) foreach { name => - val invalidEvent = Event(name) - val recipe = Recipe("EventNameTest").withSensoryEvent(invalidEvent).withInteraction(interactionOne) - - intercept[IllegalArgumentException](RecipeCompiler.compileRecipe(recipe)).getMessage.shouldBe("Event with a null or empty name found") - } - } - - "the ingredient name is null or empty" in { - List("", null) foreach { name => - val invalidIngredient = Ingredient[String](name) - val recipe = Recipe("IngredientNameTest").withSensoryEvent(Event("someEvent", invalidIngredient)).withInteraction(interactionOne) - - intercept[IllegalArgumentException](RecipeCompiler.compileRecipe(recipe)).getMessage.shouldBe("Ingredient with a null or empty name found") - } - } - - "the recipe name is null or empty" in { - List("", null) foreach { name => - val recipe = Recipe(name) - - intercept[IllegalArgumentException](RecipeCompiler.compileRecipe(recipe)).getMessage.shouldBe("Recipe with a null or empty name found") - } - } - - "an Interaction is reprovider, but has no required events" in { - val recipe: Recipe = Recipe("LoopingWithReprovider") - .withInteraction(interactionOne.isReprovider(true)) - .withSensoryEvents(initialEvent) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe List("Reprovider interaction InteractionOne needs to have a event precondition") - } - } - - "give the interaction an optional ingredient as empty" when { - "the ingredient is not provided" in { - val recipe: Recipe = Recipe("MissingOptionalRecipe") - .withInteraction(interactionWithOptionalIngredients) - .withSensoryEvent(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe List.empty - compiledRecipe.interactionTransitions - .map(it => - if (it.interactionName.equals("OptionalIngredientInteraction")) { - it.predefinedParameters.size shouldBe 4 - it.predefinedParameters("missingJavaOptional") shouldBe NullValue - it.predefinedParameters("missingJavaOptional2") shouldBe NullValue - it.predefinedParameters("missingScalaOption") shouldBe NullValue - it.predefinedParameters("missingScalaOption2") shouldBe NullValue - }) - } - } - - "give the interaction an optional ingredient with value" when { - "the ingredient is provided" in { - val optionalProviderEvent = Event("optionalProviderEvent", missingJavaOptional) - - val recipe: Recipe = Recipe("MissingOptionalRecipe") - .withInteraction(interactionWithOptionalIngredients) - .withSensoryEvents(initialEvent, optionalProviderEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe List.empty - compiledRecipe.interactionTransitions - .map(it => - if (it.interactionName.equals("OptionalIngredientInteraction")) { - it.predefinedParameters.size shouldBe 3 - it.predefinedParameters("missingJavaOptional2") shouldBe NullValue - it.predefinedParameters("missingScalaOption") shouldBe NullValue - it.predefinedParameters("missingScalaOption2") shouldBe NullValue - }) - } - - "the ingredient is renamed and provided via an event" in { - val stringOptionIngredient = Ingredient[Option[String]]("stringOptionIngredient") - val renamedStringOptionIngredient = Ingredient[Option[String]]("renamedStringOptionIngredient") - - val eventWithOptionIngredient = Event("eventWithOptionIngredient", stringOptionIngredient) - - val interactionWithOptionIngredient = Interaction("interactionWithOptionIngredient", Seq(initialIngredient), Seq(eventWithOptionIngredient)) - - val secondInteraction = Interaction("secondInteraction", Seq(renamedStringOptionIngredient), Seq()) - - val recipe = Recipe("interactionWithEventOutputTransformer") - .withSensoryEvent(initialEvent) - .withInteraction(interactionWithOptionIngredient - .withEventOutputTransformer(eventWithOptionIngredient, "RenamedEventWithOptionIngredient", Map("stringOptionIngredient" -> "renamedStringOptionIngredient"))) - .withInteraction(secondInteraction) - - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe empty - - val transition = compiledRecipe.interactionTransitions.find(_.interactionName == "secondInteraction").get - transition.nonProvidedIngredients.map(_.name) should contain("renamedStringOptionIngredient") - } - - "the ingredient is predefined" in { - val ingredientValue: Optional[String] = java.util.Optional.of("value") - val recipe: Recipe = Recipe("MissingOptionalRecipe") - .withInteraction( - interactionWithOptionalIngredients - .withPredefinedIngredients(("missingJavaOptional", ingredientValue)) - ) - .withSensoryEvents(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe List.empty - compiledRecipe.interactionTransitions - .map(it => - if (it.interactionName.equals("OptionalIngredientInteraction")) { - it.predefinedParameters.size shouldBe 4 - it.predefinedParameters("missingJavaOptional") shouldBe PrimitiveValue("value") - it.predefinedParameters("missingJavaOptional2") shouldBe NullValue - it.predefinedParameters("missingScalaOption") shouldBe NullValue - it.predefinedParameters("missingScalaOption2") shouldBe NullValue - }) - } - - "the ingredient is provided, but not wrapped in an Option type" in { - val optionalProviderEvent = Event("optionalProviderEvent", missingJavaOptionalDirectString) - - val recipe: Recipe = Recipe("MissingOptionalRecipe") - .withInteraction(interactionWithOptionalIngredients) - .withSensoryEvents(initialEvent, optionalProviderEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.validationErrors shouldBe List.empty - compiledRecipe.interactionTransitions - .map(it => - if (it.interactionName.equals("OptionalIngredientInteraction")) { - it.predefinedParameters.size shouldBe 3 - it.predefinedParameters("missingJavaOptional2") shouldBe NullValue - it.predefinedParameters("missingScalaOption") shouldBe NullValue - it.predefinedParameters("missingScalaOption2") shouldBe NullValue - }) - } - } - - "give the correct id" when { - "it compiles a java recipe" in { - val recipe = TestRecipeJava.getRecipe("id-test-recipe") - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.recipeId shouldBe "220827c42a75b3f8" - } - } - - "give the interaction with Reprovider enabled" when { - "it compiles a java recipe and changes recipeId" in { - val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.recipeId shouldBe "416e8abc02abcbee" - } - } - - "give the interaction for checkpoint-events" when { - "it compiles a java recipe" in { - val recipe = TestRecipeJava.getRecipe("id-test-recipe") - .withCheckpointEvent(CheckPointEvent("Success") - .withRequiredEvent(initialEvent)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) - compiledRecipe.recipeId shouldBe "469441173f91869a" - compiledRecipe.petriNet.transitions.count { case i: InteractionTransition => i.interactionName.contains(s"${checkpointEventInteractionPrefix}Success") case _ => false } shouldBe 1 - } - } - - "give the interaction for sub-recipes" when { - "it compiles a java recipe" in { - - val subSubRecipe: Recipe = Recipe("SubSubRecipe") - .withInteractions( - interactionOne - .withEventOutputTransformer(interactionOneSuccessful, Map("interactionOneOriginalIngredient" -> "interactionOneIngredient")) - .withFailureStrategy(InteractionFailureStrategy.RetryWithIncrementalBackoff(initialDelay = 10 millisecond, maximumRetries = 3)), - interactionTwo - .withOverriddenIngredientName("initialIngredientOld", "initialIngredient"), - ) - - val subRecipe: Recipe = Recipe("SubRecipe") - .withInteractions( - interactionThree - .withMaximumInteractionCount(1), - interactionFour - .withRequiredEvents(secondEvent, eventFromInteractionTwo), - interactionFive, - interactionSix, - ) - .withSubRecipe(subSubRecipe) - - val recipe: Recipe = Recipe("Recipe") - .withSensoryEvents( - initialEvent, - initialEventExtendedName, - secondEvent, - notUsedSensoryEvent - ) - .withInteractions( - providesNothingInteraction, - sieveInteraction - ) - .withSubRecipe(subRecipe) - - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) - - val res = compiledRecipe.petriNet.transitions - .flatMap { - case i: InteractionTransition => List(i.interactionName) - case _ => List.empty - } - .filter( _.startsWith(subRecipePrefix)) - - compiledRecipe.recipeId shouldBe "ae2282f55f0a4f9f" - res shouldBe Set( - "$SubRecipe$SubSubRecipe$InteractionOne", - "$SubRecipe$SubSubRecipe$InteractionTwo", - "$SubRecipe$SubRecipe$InteractionThree", - "$SubRecipe$SubRecipe$InteractionFour", - "$SubRecipe$SubRecipe$InteractionFive", - "$SubRecipe$SubRecipe$InteractionSix", - ) - - val vis = RecipeVisualizer.visualizeRecipe(compiledRecipe, RecipeVisualStyle.default) - - println(vis) - } - } - } -} diff --git a/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala b/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala index 1b5bb2913..e96239e60 100644 --- a/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala +++ b/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala @@ -3,11 +3,10 @@ package com.ing.baker.il import com.ing.baker.il.petrinet.InteractionTransition import com.ing.baker.petrinet.api._ import com.ing.baker.types -import scala.collection.immutable.Seq object RecipeValidations { - def validateInteraction(compiledRecipe: CompiledRecipe)(interactionTransition: InteractionTransition): Seq[String] = { + private def validateInteraction(compiledRecipe: CompiledRecipe)(interactionTransition: InteractionTransition): Seq[String] = { val interactionWithNoRequirementsValidation : Seq[String] = Some(s"Interaction $interactionTransition does not have any requirements (ingredients or preconditions)! This will result in an infinite execution loop.") .filter(_ => compiledRecipe.petriNet.inMarking(interactionTransition).isEmpty).toIndexedSeq @@ -60,7 +59,7 @@ object RecipeValidations { predefinedIngredientOfExpectedTypeValidation } - def validateInteractions(compiledRecipe: CompiledRecipe): Seq[String] = { + private def validateInteractions(compiledRecipe: CompiledRecipe): Seq[String] = { compiledRecipe.interactionTransitions.toIndexedSeq.flatMap(validateInteraction(compiledRecipe)) } @@ -68,7 +67,7 @@ object RecipeValidations { /** * Validates that all required ingredients for interactions are provided for and of the correct type. */ - def validateInteractionIngredients(compiledRecipe: CompiledRecipe): Seq[String] = { + private def validateInteractionIngredients(compiledRecipe: CompiledRecipe): Seq[String] = { compiledRecipe.interactionTransitions.toIndexedSeq.flatMap { t => t.nonProvidedIngredients.flatMap { case (IngredientDescriptor(name, expectedType)) => @@ -88,18 +87,18 @@ object RecipeValidations { /** * Validates that provided ingredients do not contain reserved names for Baker */ - def validateSpecialIngredientsNotProvided(compiledRecipe: CompiledRecipe): Seq[String] = { + private def validateSpecialIngredientsNotProvided(compiledRecipe: CompiledRecipe): Seq[String] = { compiledRecipe.allIngredients.filter(i => i.name == recipeInstanceIdName || i.name == recipeInstanceMetadataName || i.name == recipeInstanceEventListName ).map(i => s"Ingredient '${i.name}' is provided and this is a reserved name for internal use in Baker") }.toSeq - def validateNoCycles(compiledRecipe: CompiledRecipe): Seq[String] = { + private def validateNoCycles(compiledRecipe: CompiledRecipe): Seq[String] = { val cycle: Option[compiledRecipe.petriNet.innerGraph.Cycle] = compiledRecipe.petriNet.innerGraph.findCycle cycle.map(c => s"The petrinet topology contains a cycle: $c").toList } - def validateAllInteractionsExecutable(compiledRecipe: CompiledRecipe): Seq[String] = { + private def validateAllInteractionsExecutable(compiledRecipe: CompiledRecipe): Seq[String] = { val rootNode = PetriNetAnalysis.calculateCoverabilityTree(compiledRecipe.petriNet, compiledRecipe.initialMarking.multiplicities) compiledRecipe.interactionTransitions filterNot { interaction => From 12b57483f2b0067301e009f82ab10c8dfa09e323 Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Wed, 18 Mar 2026 12:28:33 +0100 Subject: [PATCH 04/16] removing scala recipe compiler --- .../test/java/com/ing/baker/BakerTest.java | 8 +- .../src/test/java/com/ing/baker/Webshop.java | 2 +- .../compiler/JavaCompiledRecipeTest.java | 12 +- .../com/ing/baker/BakerRuntimeTestBase.scala | 2 +- .../ing/baker/il/RecipeVisualizerSpec.scala | 8 +- .../ing/baker/pbt/RecipePropertiesSpec.scala | 2 +- .../com/ing/baker/runtime/ExamplesSpec.scala | 6 +- .../runtime/akka/BakerExecutionSpec.scala | 6 +- .../baker/runtime/akka/BakerInquireSpec.scala | 4 +- .../baker/runtime/akka/BakerSetupSpec.scala | 22 +- .../SensoryEventResponseHandlerSpec.scala | 2 +- .../RecipeManagerActorSpec.scala | 2 +- .../serialization/SerializationSpec.scala | 2 +- core/baker-compiler/pom.xml | 28 +- .../com/ing/baker/compiler/RecipeCompiler.kt | 27 + .../baker/compiler/RecipeCompilerKotlin.kt | 4 - .../ing/baker/compiler/RecipeCompiler.scala | 16 - .../baker/compiler/RecipeCompilerScala.scala | 416 ----------- .../com/ing/baker/compiler/package.scala | 129 ---- .../baker/compiler/RecipeCompilerTests.java | 225 ------ .../src/test/kotlin/RecipeCompilerSpecTest.kt | 644 ----------------- .../baker/compiler/RecipeCompilerBasicTest.kt | 195 +++++ ...mpilerTest.kt => RecipeCompilerDslTest.kt} | 5 +- ...cTest.kt => RecipeCompilerFeaturesTest.kt} | 87 +-- core/baker-interface-kotlin/pom.xml | 32 +- .../src/test/kotlin/BakerInterfaceTest.kt | 5 +- .../inmemory/InMemoryMemoryCleanupSpec.scala | 12 +- .../baker/runtime/model/BakerModelSpec.scala | 2 +- .../model/BakerModelSpecEdgeCasesTests.scala | 2 +- .../model/BakerModelSpecEnquireTests.scala | 4 +- .../model/BakerModelSpecSetupTests.scala | 24 +- .../recipe/scaladsl/CheckPointEvent.scala | 4 - .../ing/baker/test/recipe/WebshopBaker.scala | 2 +- .../ing/baker/compiler/AssertionsKotlin.kt | 51 -- .../baker/compiler/RecipeCompilerKotlin.kt | 680 ------------------ .../ing/baker/compiler/ScalaConversions.kt | 20 - docs/archive/baker-runtime.md | 6 +- docs/archive/recipe-visualization.md | 4 +- docs/sections/reference/main-abstractions.md | 4 +- docs/sections/reference/runtime.md | 4 +- .../webshop/webservice/WebShopBaker.scala | 2 +- .../java/webshop/JWebshopRecipeTests.java | 8 +- .../webshop/simple/WebshopRecipeSpec.scala | 8 +- .../main/scala/webshop/webservice/Main.scala | 2 +- .../webshop/webservice/WebShopBaker.scala | 2 +- .../examples/java/application/WebShopApp.java | 2 +- .../visualization/WebShopVisualization.java | 2 +- .../examples/kotlin/application/WebShopApp.kt | 2 +- .../scala/application/WebShopApp.scala | 2 +- .../visualization/WebShopVisualization.scala | 2 +- .../java/recipes/WebShopRecipeTest.java | 2 +- .../scala/recipes/WebShopRecipeTest.scala | 2 +- .../http/server/common/RecipeLoaderSpec.scala | 2 +- 53 files changed, 369 insertions(+), 2379 deletions(-) create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt delete mode 100644 core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala delete mode 100644 core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala delete mode 100644 core/baker-compiler/src/main/scala/com/ing/baker/compiler/package.scala delete mode 100644 core/baker-compiler/src/test/java/com/ing/baker/compiler/RecipeCompilerTests.java delete mode 100644 core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt create mode 100644 core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt rename core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/{RecipeCompilerTest.kt => RecipeCompilerDslTest.kt} (90%) rename core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/{RecipeCompilerSpecTest.kt => RecipeCompilerFeaturesTest.kt} (91%) delete mode 100644 core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt delete mode 100644 core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt delete mode 100644 core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java index 2cd737a1f..7655c20ac 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java @@ -57,7 +57,7 @@ public static void tearDown() { @Test public void shouldSetupJBakerWithDefaultActorFramework() throws BakerException, ExecutionException, InterruptedException { - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); String recipeInstanceId = UUID.randomUUID().toString(); Baker jBaker = AkkaBaker.java(config, actorSystem, implementationsList); @@ -79,7 +79,7 @@ public void shouldSetupJBakerWithDefaultActorFramework() throws BakerException, @Test public void shouldSetupJBakerWithGivenActorFramework() throws BakerException, ExecutionException, InterruptedException { - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); assertEquals(compiledRecipe.getValidationErrors().size(), 0); @@ -101,7 +101,7 @@ public void shouldSetupJBakerWithGivenActorFramework() throws BakerException, Ex public void shouldFailWhenMissingImplementations() throws BakerException, ExecutionException, InterruptedException { exception.expect(ExecutionException.class); - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); Baker jBaker = AkkaBaker.java(config, actorSystem); jBaker.addRecipe(RecipeRecord.of(compiledRecipe, System.currentTimeMillis(), true, true)).get(); @@ -118,7 +118,7 @@ public void shouldExecuteCompleteFlow() throws BakerException, ExecutionExceptio jBaker.registerBakerEventListener(bakerEvents::add); // Setup recipe - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); String recipeId = jBaker.addRecipe(RecipeRecord.of(compiledRecipe, System.currentTimeMillis(), false, true)).get(); EventInstance eventOne = EventInstance.from(new JavaCompiledRecipeTest.EventOne()); assertEquals(eventOne.getName(), "EventOne"); diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java index 181a494da..84a427467 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java @@ -138,7 +138,7 @@ public void testWebshop() throws ExecutionException, InterruptedException { Config config = ConfigFactory.load("cassandra.conf"); - CompiledRecipe recipe = RecipeCompiler.compileRecipe(webshopRecipe); + CompiledRecipe recipe = RecipeCompiler.INSTANCE.compileRecipe(webshopRecipe); System.out.println(recipe.getRecipeVisualization()); diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java index 3b0825514..3b0097414 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java @@ -26,7 +26,7 @@ public class JavaCompiledRecipeTest { public void shouldCompileSimpleRecipe() throws BakerException { Recipe recipe = setupSimpleRecipe(); - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); assertEquals(compiledRecipe.getValidationErrors(), new ArrayList()); assertEquals("EventOne", recipe.getEvents().get(0).name()); assertEquals("InteractionOne", recipe.getInteractions().get(0).name()); @@ -37,7 +37,7 @@ public void shouldCompileSimpleRecipe() throws BakerException { @Test public void shouldCompileComplexRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); assertEquals(compiledRecipe.getValidationErrors(), new ArrayList()); assertEquals("EventOne", recipe.getEvents().get(0).name()); assertEquals("EventTwo", recipe.getEvents().get(1).name()); @@ -52,7 +52,7 @@ public void shouldCompileComplexRecipe() throws BakerException { @Test public void shouldShowVisualRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); assertEquals(compiledRecipe.getValidationErrors().size(), 0); String visualRecipe = compiledRecipe.getRecipeVisualization(); Assert.assertTrue("Should contain actionOne", visualRecipe.contains("actionOne")); @@ -67,7 +67,7 @@ public void shouldShowVisualRecipe() throws BakerException { @Test public void shouldShowPetriNetVisual() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compileRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compileRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); assertEquals(compileRecipe.getValidationErrors().size(), 0); String petrinetVisual = compileRecipe.getPetriNetVisualization(); Assert.assertTrue("Should contain actionOne", petrinetVisual.contains("actionOne")); @@ -80,7 +80,7 @@ public void shouldShowPetriNetVisual() throws BakerException { @Test public void shouldShowFilteredVisualRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compileRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compileRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); assertEquals(compileRecipe.getValidationErrors().size(), 0); String visualRecipe = compileRecipe.getFilteredRecipeVisualization("InteractionOne"); Assert.assertTrue("Should not contain actionOne", !visualRecipe.contains("InteractionOne")); @@ -94,7 +94,7 @@ public void shouldShowFilteredVisualRecipe() throws BakerException { @Test public void shouldShowFilteredMultipleVisualRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compileRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compileRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); assertEquals(compileRecipe.getValidationErrors().size(), 0); String visualRecipe = compileRecipe.getFilteredRecipeVisualization(new String[]{"actionOne", "EventTwo"}); Assert.assertTrue("Should not contain actionOne", !visualRecipe.contains("actionOne")); diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala index 94950cd2b..f59885b0c 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala @@ -217,7 +217,7 @@ trait BakerRuntimeTestBase protected def setupBakerWithRecipe(recipe: Recipe, implementations: List[InteractionInstance]) (implicit actorSystem: ActorSystem): Future[(Baker, String)] = { val baker = AkkaBaker(ConfigFactory.load(), actorSystem, CachingInteractionManager(implementations)) - baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).map(baker -> _)(actorSystem.dispatcher) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).map(baker -> _)(actorSystem.dispatcher) } protected def setupBakerWithNoRecipe()(implicit actorSystem: ActorSystem): Future[Baker] = { diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala index 34a01fbf1..f64485c33 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala @@ -14,7 +14,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { "be able to visualize a a created compile recipe" in { val recipe: Recipe = getRecipe("VisualizationRecipe") - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compiledRecipe, RecipeVisualStyle.default) dot should include("interactionOneIngredient -> InteractionThree") @@ -23,7 +23,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { "be able to visualize the created interactions with a filter" in { val recipe: Recipe = getRecipe("filteredVisualRecipe") - val compileRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compileRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compileRecipe, RecipeVisualStyle.default, filter = e => !e.contains("interactionFour")) dot shouldNot contain("interactionFour") } @@ -32,7 +32,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { val recipe: Recipe = Recipe("missingEvent") .withInteraction(interactionOne.withRequiredEvent(secondEvent)) .withSensoryEvent(initialEvent) - val compileRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compileRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compileRecipe, RecipeVisualStyle.default) dot should include("#EE0000") } @@ -41,7 +41,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { val recipe: Recipe = Recipe("missingEvent") .withInteraction(interactionOne) .withSensoryEvent(secondEvent) - val compileRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compileRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compileRecipe, RecipeVisualStyle.default) dot should include("#EE0000") } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala index 0afc032fe..675e388bc 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala @@ -24,7 +24,7 @@ class RecipePropertiesSpec extends AnyFunSuite with Checkers { val prop = forAll(recipeGen) { recipe => val validations = ValidationSettings(allowCycles = false, allowNonExecutableInteractions = false) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe, validations) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe, validations) if (compiledRecipe.validationErrors.nonEmpty) { logRecipeStats(recipe) diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala index b6ea7e6cc..2e4c9d22a 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala @@ -24,7 +24,7 @@ class ExamplesSpec extends BakerRuntimeTestBase { "compile without validation errors" in { // compiles the recipe - val compiledRecipe = RecipeCompiler.compileRecipe(webShopRecipe) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(webShopRecipe) // println(s"Visual recipe: ${compiledRecipe.getRecipeVisualization}") @@ -35,7 +35,7 @@ class ExamplesSpec extends BakerRuntimeTestBase { "run a happy flow" in { // compiles the recipe - val compiledRecipe = RecipeCompiler.compileRecipe(webShopRecipe) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(webShopRecipe) // test data val testCustomerInfoData = CustomerInfo("John Snow", "Winterfell", "john_snow@hotmail.com") @@ -107,7 +107,7 @@ class ExamplesSpec extends BakerRuntimeTestBase { "compile without validation errors" in { // compiles the recipe - val compiledRecipe = RecipeCompiler.compileRecipe(openAccountRecipe) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(openAccountRecipe) // prints any validation errors the compiler found Future { compiledRecipe.validationErrors shouldBe empty } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala index 257833393..f02b59b5e 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala @@ -389,7 +389,7 @@ class BakerExecutionSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(config, ActorSystem.apply("remoteTest", config), CachingInteractionManager(mockImplementations)) for { - recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) _ = when(testInteractionOneMock.apply(anyString(), anyString())).thenReturn(Future.successful(InteractionOneSuccessful(interactionOneIngredientValue))) recipeInstanceId = UUID.randomUUID().toString _ <- baker.bake(recipeId, recipeInstanceId) @@ -718,7 +718,7 @@ class BakerExecutionSpec extends BakerRuntimeTestBase { .withSensoryEvent(initialEvent) val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(mockImplementations)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) for { recipeId <- baker.addRecipe(RecipeRecord.of(compiledRecipe)) @@ -1360,7 +1360,7 @@ class BakerExecutionSpec extends BakerRuntimeTestBase { val recoveryRecipeName = "RecoveryRecipe" val recipeInstanceId = UUID.randomUUID().toString - val compiledRecipe = RecipeCompiler.compileRecipe(getRecipe(recoveryRecipeName)) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(getRecipe(recoveryRecipeName)) val first = (for { baker1 <- setupBakerWithNoRecipe()(system1) diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala index 8cfd28130..913874b59 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala @@ -35,7 +35,7 @@ class BakerInquireSpec extends BakerRuntimeTestBase { "return all recipes if asked" in { for { (baker, recipeId) <- setupBakerWithRecipe("returnAllRecipes", false) - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnAllRecipes2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnAllRecipes2")))) recipes <- baker.getAllRecipes _ = recipes.size shouldBe 2 _ = recipes(recipeId).compiledRecipe.name shouldBe "returnAllRecipes" @@ -54,7 +54,7 @@ class BakerInquireSpec extends BakerRuntimeTestBase { "return no errors of all recipes if none contain errors if asked" in { for { (baker, recipeId) <- setupBakerWithRecipe("returnHealthAllRecipe", false) - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnHealthAllRecipe2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnHealthAllRecipe2")))) recipeInformations <- baker.getAllRecipes _ = recipeInformations.size shouldBe 2 _ = recipeInformations.get(recipeId) diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala index 72787e040..6a107be63 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala @@ -37,7 +37,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { "bootstrap correctly without throwing an error if provided a correct recipe and correct implementations" when { "correctly load extensions when specified in the configuration" in { - val simpleRecipe = RecipeCompiler.compileRecipe(Recipe("SimpleRecipe") + val simpleRecipe = RecipeCompiler.INSTANCE.compileRecipe(Recipe("SimpleRecipe") .withInteraction(interactionOne) .withSensoryEvent(initialEvent)) @@ -74,7 +74,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new implementations.InteractionOne()))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } @@ -87,7 +87,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new InteractionOneFieldName()))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } @@ -100,7 +100,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } @@ -112,7 +112,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(mock[ComplexIngredientInteraction]))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } } @@ -127,8 +127,8 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(mockImplementations)) recoverToExceptionIf[RecipeValidationException] { - baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe NonProvidedIngredient:68b775e508fc6877 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction"))) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + }.map(_ should have('message("Recipe NonProvidedIngredient:f31b181c8e36a5c3 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction"))) } "a recipe does not provide an implementation for an interaction" in { @@ -140,8 +140,8 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager()) recoverToExceptionIf[ImplementationsException] { - baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe MissingImplementation:dc3970efc8837e64 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)"))) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + }.map(_ should have('message("Recipe MissingImplementation:e1b92d7afa5609d5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)"))) } "a recipe provides an implementation for an interaction and does not comply to the Interaction" in { @@ -153,8 +153,8 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new InteractionOneWrongApply()))) recoverToExceptionIf[ImplementationsException] { - baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe WrongImplementation:8e2745de0bb0bde5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)"))) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + }.map(_ should have('message("Recipe WrongImplementation:7fcdb6b8feb8aa87 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)"))) } } } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala index 861b18a38..6f1691560 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala @@ -27,7 +27,7 @@ class SensoryEventResponseHandlerSpec extends AkkaTestBase("SensoryEventResponse import com.ing.baker.recipe.scaladsl._ import Examples.webshop - val webShopRecipe = RecipeCompiler.compileRecipe(webshop.webShopRecipe) + val webShopRecipe = RecipeCompiler.INSTANCE.compileRecipe(webshop.webShopRecipe) "return a SensoryEventResult when processing the outcome of a sensory event with NotifyWhenCompleted reaction" in { val client = TestProbe() diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala index cdec5bfa1..7de824757 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala @@ -25,7 +25,7 @@ class RecipeManagerActorSpec extends BakerRuntimeTestBase { "The recipe manager" should { "add a recipe to the list when an AddRecipe message is received" in { - val compiledRecipe = RecipeCompiler.compileRecipe(TestRecipe.getRecipe("AddRecipeRecipe")) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(TestRecipe.getRecipe("AddRecipeRecipe")) val recipeManager: ActorRef = defaultActorSystem.actorOf(RecipeManagerActor.props(), s"recipeManager-${UUID.randomUUID().toString}") for { diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala index f6a722372..27f522e2c 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala @@ -263,7 +263,7 @@ object SerializationSpec { val eventNameGen: Gen[String] = Gen.alphaStr val finiteDurationGen: Gen[FiniteDuration] = Gen.posNum[Long].map(millis => FiniteDuration(millis, TimeUnit.MILLISECONDS)) - val allTypesRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(AllTypeRecipe.recipe) + val allTypesRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(AllTypeRecipe.recipe) implicit val recipeGen: Gen[CompiledRecipe] = Gen.const(allTypesRecipe) } diff --git a/core/baker-compiler/pom.xml b/core/baker-compiler/pom.xml index ec50379f6..c395fe315 100644 --- a/core/baker-compiler/pom.xml +++ b/core/baker-compiler/pom.xml @@ -32,11 +32,6 @@ ${project.version} - - org.scala-lang - scala-library - - com.ing.baker @@ -46,16 +41,6 @@ test - - org.scalatest - scalatest_${scala.compat.version} - test - - - org.scalacheck - scalacheck_${scala.compat.version} - test - org.junit.jupiter junit-jupiter-engine @@ -71,7 +56,7 @@ - + org.codehaus.mojo build-helper-maven-plugin @@ -97,7 +82,6 @@ - src/test/java src/test/kotlin @@ -105,11 +89,6 @@ - - net.alchim31.maven - scala-maven-plugin - - org.jetbrains.kotlin kotlin-maven-plugin @@ -134,11 +113,6 @@ - - org.scalatest - scalatest-maven-plugin - - org.apache.maven.plugins diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt new file mode 100644 index 000000000..c28d429ea --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt @@ -0,0 +1,27 @@ +package com.ing.baker.compiler + +import com.ing.baker.il.CompiledRecipe +import com.ing.baker.il.ValidationSettings +import com.ing.baker.recipe.common.Recipe + +object RecipeCompiler { + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @return + */ + fun compileRecipe(recipe: Recipe): CompiledRecipe = + compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + fun compileRecipe(recipe: Recipe, validationSettings: ValidationSettings): CompiledRecipe = + RecipeCompilerKotlin.compileRecipe(recipe, validationSettings) +} diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt index 5c9dc14f7..e3d89285e 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt @@ -56,7 +56,6 @@ import scalax.collection.mutable.ArraySet import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleJava$`.`MODULE$` as Scala212CompatibleJava import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleKotlin$`.`MODULE$` as Scala212CompatibleKotlin import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` as Scala212CompatibleScala -import com.ing.baker.il.`ValidationSettings$`.`MODULE$` as ValidationSettings import com.ing.baker.il.failurestrategy.InteractionFailureStrategy as ILInteractionFailureStrategy import com.ing.baker.il.`package$`.`MODULE$` as ILPackage import com.ing.baker.il.petrinet.Place.`EmptyEventIngredientPlace$`.`MODULE$` as EmptyEventIngredientPlace @@ -462,9 +461,6 @@ object RecipeCompilerKotlin { return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) } - fun compileRecipe(recipe: Recipe): CompiledRecipe = - compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) - private fun getMultiTransition(internalRepresentationName: String, transitions: List) = transitions.find { it.label().equals(internalRepresentationName) } ?: throw NoSuchElementException("No multi transition found with name $internalRepresentationName") diff --git a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala deleted file mode 100644 index 49caab906..000000000 --- a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompiler.scala +++ /dev/null @@ -1,16 +0,0 @@ -package com.ing.baker -package compiler - -import com.ing.baker.il.{CompiledRecipe, ValidationSettings} -import com.ing.baker.recipe.common._ - -import scala.language.postfixOps - -object RecipeCompiler { - - def compileRecipe(recipe: Recipe): CompiledRecipe = - compileRecipe(recipe, ValidationSettings.defaultValidationSettings) - - def compileRecipe(recipe: Recipe, validationSettings: ValidationSettings): CompiledRecipe = - RecipeCompilerScala.compileRecipe(recipe, validationSettings) -} diff --git a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala deleted file mode 100644 index 9d4406aac..000000000 --- a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala +++ /dev/null @@ -1,416 +0,0 @@ -package com.ing.baker.compiler - -import com.ing.baker.il.CompiledRecipe.{OldRecipeIdVariant, Scala212CompatibleJava, Scala212CompatibleKotlin, Scala212CompatibleScala} -import com.ing.baker.il.RecipeValidations.postCompileValidations -import com.ing.baker.il.petrinet.Place._ -import com.ing.baker.il.petrinet._ -import com.ing.baker.il.{CompiledRecipe, EventDescriptor, ValidationSettings, checkpointEventInteractionPrefix, sieveInteractionPrefix, subRecipePrefix} -import com.ing.baker.petrinet.api._ -import com.ing.baker.recipe.common._ -import com.ing.baker.recipe.{javadsl, kotlindsl} -import com.ing.baker.recipe.scaladsl.{Event, Interaction} -import scalax.collection.edge.WLDiEdge -import scalax.collection.immutable.Graph - -import scala.annotation.nowarn -import scala.language.postfixOps - -private object RecipeCompilerScala { - - implicit class TupleSeqOps[A, B](seq: Seq[(Seq[A], Seq[B])]) { - def unzipFlatten: (Seq[A], Seq[B]) = seq.unzip match { - case (a, b) => (a.flatten, b.flatten) - } - } - - @nowarn - def arc(t: Transition, p: Place, weight: Long): Arc = WLDiEdge[Node, Edge](Right(t), Left(p))(weight, Edge(None)) - - @nowarn - def arc(p: Place, t: Transition, weight: Long, eventFilter: Option[String] = None): Arc = { - WLDiEdge[Node, Edge](Left(p), Right(t))(weight, Edge(eventFilter)) - } - - /** - * Creates a transition for a missing event in the recipe. - */ - private def missingEventTransition[E](eventName: String): MissingEventTransition = MissingEventTransition(eventName) - - private def buildEventAndPreconditionArcs(interaction: InteractionDescriptor, - preconditionTransition: String => Option[Transition], - interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { - - //Find the event in available events - - interaction.requiredEvents.toIndexedSeq.map { eventName => - // a new `Place` generated for each AND events - val eventPreconditionPlace = createPlace(label = s"$eventName-${interaction.name}", placeType = EventPreconditionPlace) - - buildEventPreconditionArcs(eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name).get) - }.unzipFlatten - } - - private def buildEventORPreconditionArcs(interaction: InteractionDescriptor, - preconditionTransition: String => Option[Transition], - interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { - - interaction.requiredOneOfEvents.toIndexedSeq.zipWithIndex.map { case (orGroup: Set[String], index: Int) => - // only one `Place` for all the OR events - val eventPreconditionPlace = createPlace(label = s"${interaction.name}-or-$index", placeType = EventOrPreconditionPlace) - - orGroup.toIndexedSeq.map { eventName => - buildEventPreconditionArcs(eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name).get) - }.unzipFlatten - }.unzipFlatten - } - - private def buildEventPreconditionArcs(eventName: String, - preconditionPlace: Place, - preconditionTransition: String => Option[Transition], - interactionTransition: Transition): (Seq[Arc], Seq[String]) = { - - val eventTransition = preconditionTransition(eventName) - - val notProvidedError = eventTransition match { - case None => Seq(s"Event '$eventName' for '$interactionTransition' is not provided in the recipe") - case Some(_) => Seq.empty - } - - val arcs = Seq( - arc(eventTransition.getOrElse(missingEventTransition(eventName)), preconditionPlace, 1), - arc(preconditionPlace, interactionTransition, 1) - ) - - (arcs, notProvidedError) - } - - // the (possible) event output arcs / places - private def buildInteractionOutputArcs(interaction: InteractionTransition, - eventTransitions: Seq[EventTransition]): Seq[Arc] = { - val resultPlace = createPlace(label = interaction.label, placeType = InteractionEventOutputPlace) - if (interaction.eventsToFire.nonEmpty) { - val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor => - //Get the correct event transition - val eventTransition = eventTransitions.find(_.event.name == event.name).get - //Decide if there are multiple interactions that fire this transition, - // if so create a event combiner place - // else link the transition to the event. - val eventTransitionCount = eventTransitions.count(e => e.event.name == event.name) - if (eventTransitionCount > 1) { - //Create a new intermediate event place - val eventCombinerPlace: Place = createPlace(label = event.name, placeType = IntermediatePlace) - //Create a new intermediate event transition - val interactionToEventTransition: IntermediateTransition = IntermediateTransition(s"${interaction.interactionName}:${event.name}") - //link the interaction output place to the intermediate transition - val interactionOutputPlaceToIntermediateTransition: Arc = arc(resultPlace, interactionToEventTransition, 1, Some(event.name)) - //link the intermediate transition to the intermediate input place - val intermediateTransitionToEventCombinerPlace: Arc = arc(interactionToEventTransition, eventCombinerPlace, 1) - //Link the intermediate place to the event place - val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) - Seq(intermediateTransitionToEventCombinerPlace, interactionOutputPlaceToIntermediateTransition, eventCombinerPlaceToEventTransition) - } - else { - val internalEventTransition = eventTransition - Seq(arc(resultPlace, internalEventTransition, 1, Some(event.name))) - } - } - arc(interaction, resultPlace, 1) +: eventArcs - } - else Seq.empty - } - - - /** - * Draws an arc from all required ingredients for an interaction - * If the ingredient has multiple consumers create a multi transition place and create both arcs for it - */ - private def buildInteractionInputArcs(t: InteractionTransition, - multipleConsumerFacilitatorTransitions: Seq[Transition], - ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]]): Seq[Arc] = { - - val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = - t.nonProvidedIngredients.map(_.name).partition(ingredientsWithMultipleConsumers.contains) - - // the extra arcs to model multiple output transitions from one place - val internalDataInputArcs: Seq[Arc] = fieldNamesWithPrefixMulti flatMap { fieldName => - val multiTransitionPlace = createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace) - Seq( - // one arc from multiplier place to the transition - arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), - multiTransitionPlace, - 1), - // one arc from extra added place to transition - arc(multiTransitionPlace, t, 1)) - } - - // the data input arcs / places - val dataInputArcs: Seq[Arc] = fieldNamesWithoutPrefix.map(fieldName => arc(createPlace(fieldName, IngredientPlace), t, 1)) - - val dataOutputArcs: Seq[Arc] = - if (t.isReprovider) - fieldNamesWithoutPrefix.map(fieldName => arc(t, createPlace(fieldName, IngredientPlace), 1)) ++ - fieldNamesWithPrefixMulti.map(fieldName => arc(t, createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace), 1)) - else - Seq.empty - - val limitInteractionCountArc: Option[Arc] = - t.maximumInteractionCount.map(n => arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) - - dataInputArcs ++ dataOutputArcs ++ internalDataInputArcs ++ limitInteractionCountArc - } - - private def buildInteractionArcs(multipleOutputFacilitatorTransitions: Seq[Transition], - placeNameWithDuplicateTransitions: Map[String, Seq[InteractionTransition]], - eventTransitions: Seq[EventTransition]) - (t: InteractionTransition): Seq[Arc] = { - - val inputArcs: Seq[Arc] = buildInteractionInputArcs(t, multipleOutputFacilitatorTransitions, placeNameWithDuplicateTransitions) - - val outputArcs: Seq[Arc] = buildInteractionOutputArcs(t, eventTransitions) - - inputArcs ++ outputArcs - } - - /** - * Compile the given recipe to a technical recipe that is useful for Baker. - * - * @param recipe ; The Recipe to compile and execute - * @param validationSettings The validation settings to follow for the validation - * @return - */ - def compileRecipe(recipe: Recipe, - validationSettings: ValidationSettings): CompiledRecipe = { - - val precompileErrors: Seq[String] = PreCompileValidations.INSTANCE.preCompileAssertions(recipe) - - // Extend the interactions with the checkpoint event interactions and sub-recipes - val actionDescriptors: Seq[InteractionDescriptor] = recipe.interactions ++ - recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ - recipe.sieves.map(convertSieveToInteraction) ++ - recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) - - // Flatten all sensory events from sub recipes - val sensoryEvents = flattenSensoryEvents(recipe) - - //All ingredient names provided by sensory events or by interactions - val allIngredientNames: Set[String] = - sensoryEvents.flatMap(e => e.providedIngredients.map(i => i.name)) ++ - actionDescriptors.flatMap(i => i.output.flatMap { e => - // check if the event was renamed (check if there is a transformer for this event) - i.eventOutputTransformers.get(e) match { - case Some(transformer) => e.providedIngredients.map(ingredient => transformer.ingredientRenames.getOrElse(ingredient.name, ingredient.name)) - case None => e.providedIngredients.map(_.name) - } - } - ) - - // For inputs for which no matching output cannot be found, we do not want to generate a place. - // It should be provided at runtime from outside the active petri net (marking) - val interactionTransitions = actionDescriptors.map(_.toInteractionTransition(recipe.defaultFailureStrategy, allIngredientNames)) - - val allInteractionTransitions: Seq[InteractionTransition] = interactionTransitions - - // events provided from outside - val sensoryEventTransitions: Seq[EventTransition] = sensoryEvents.map { - event => EventTransition(eventToCompiledEvent(event), isSensoryEvent = true, event.maxFiringLimit) - }.toIndexedSeq - - // events provided by other transitions / actions - val interactionEventTransitions: Seq[EventTransition] = allInteractionTransitions.flatMap { t => - t.eventsToFire.map(event => EventTransition(event, isSensoryEvent = false)) - } - - val allEventTransitions: Seq[EventTransition] = sensoryEventTransitions ++ interactionEventTransitions - - // Given the event classes, it is creating the ingredient places and - // connecting a transition to a ingredient place. - val internalEventArcs: Seq[Arc] = allInteractionTransitions.flatMap { t => - t.eventsToFire.flatMap { event => - event.ingredients.map { ingredient => - val from = interactionEventTransitions.find(_.label == event.name).get - arc(from, createPlace(ingredient.name, IngredientPlace), 1) - } - } - } - - //Create event limiter places so that events can only fire x amount of times. - val eventLimiterArcs: Seq[Arc] = sensoryEventTransitions.flatMap( - t => t.maxFiringLimit match { - case Some(n) => Seq(arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) - case None => Seq.empty - } - ) - - def findEventTransitionByEventName(eventName: String) = allEventTransitions.find(_.event.name == eventName) - - def findInteractionByLabel(label: String) = allInteractionTransitions.find(_.label == label) - - // This generates precondition arcs for Required Events (AND). - val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t => - buildEventAndPreconditionArcs(t, - findEventTransitionByEventName, - findInteractionByLabel) - }.unzipFlatten - - // This generates precondition arcs for Required Events (OR). - val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t => - buildEventORPreconditionArcs(t, - findEventTransitionByEventName, - findInteractionByLabel) - }.unzipFlatten - - val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition(_.event.ingredients.isEmpty) - - // It connects a sensory event to an ingredient places - val sensoryEventArcs: Seq[Arc] = sensoryEventWithIngredients - .flatMap(et => et.event.ingredients.map(ingredient => arc(et, createPlace(ingredient.name, IngredientPlace), 1))) - - val eventThatArePreconditions: Seq[String] = - actionDescriptors.flatMap { - actionDescriptor => actionDescriptor.requiredEvents ++ actionDescriptor.requiredOneOfEvents.flatten - } - - // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net - val sensoryEventArcsNoIngredientsArcs: Seq[Arc] = sensoryEventWithoutIngredients - //Filter out events that are preconditions to interactions - .filterNot(sensoryEvent => eventThatArePreconditions.contains(sensoryEvent.label)) - .map(sensoryEvent => arc(sensoryEvent, createPlace(sensoryEvent.label, EmptyEventIngredientPlace), 1)) - - // First find the cases where multiple transitions depend on the same ingredient place - val ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]] = - getIngredientsWithMultipleConsumers(allInteractionTransitions) - - // Add one new transition for each duplicate input (the newly added one in the image above) - val multipleConsumerFacilitatorTransitions: Seq[Transition] = - ingredientsWithMultipleConsumers.keys - .map(name => MultiFacilitatorTransition(label = name)) - .toIndexedSeq - - val multipleOutputFacilitatorArcs: Seq[Arc] = - multipleConsumerFacilitatorTransitions.map(t => - arc(createPlace(t.label, IngredientPlace), t, 1)) - - val interactionArcs: Seq[Arc] = - allInteractionTransitions.flatMap( - buildInteractionArcs( - multipleConsumerFacilitatorTransitions, - ingredientsWithMultipleConsumers, - interactionEventTransitions)) - - val arcs = (interactionArcs - ++ eventPreconditionArcs - ++ eventOrPreconditionArcs - ++ eventLimiterArcs - ++ sensoryEventArcs - ++ sensoryEventArcsNoIngredientsArcs - ++ internalEventArcs - ++ multipleOutputFacilitatorArcs) - - val petriNet: PetriNet = new PetriNet(Graph(arcs: _*)) - - val initialMarking: Marking[Place] = petriNet.places.collect { - case p@Place(_, FiringLimiterPlace(n)) => p -> Map[Any, Int]((null, n)) - }.toMarking - - val errors = preconditionORErrors ++ preconditionANDErrors ++ precompileErrors - - val oldRecipeIdVariant: OldRecipeIdVariant = - recipe match { - case _: javadsl.Recipe => Scala212CompatibleJava - case _: kotlindsl.Recipe => Scala212CompatibleKotlin - case _ => Scala212CompatibleScala - } - - val compiledRecipe = CompiledRecipe.build( - name = recipe.name, - petriNet = petriNet, - initialMarking = initialMarking, - validationErrors = errors, - eventReceivePeriod = recipe.eventReceivePeriod, - retentionPeriod = recipe.retentionPeriod, - oldRecipeIdVariant = oldRecipeIdVariant, - ) - - postCompileValidations(compiledRecipe, validationSettings) - } - - private def getMultiTransition(internalRepresentationName: String, transitions: Seq[Transition]): Transition = - transitions - .find(t => t.label.equals(internalRepresentationName)) - .getOrElse(throw new NoSuchElementException(s"No multi transition found with name $internalRepresentationName")) - - /** - * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. - * - * @param actionTransitions Seq of reflected transition. - * @return A map from input place name to reflected transitions (where the transitions have as input the place). - */ - private def getIngredientsWithMultipleConsumers(actionTransitions: Seq[InteractionTransition]): Map[String, Seq[InteractionTransition]] = { - // Obtain a list of field name with their transition - val placeNameWithTransition: Seq[(String, InteractionTransition)] = for { - transition <- actionTransitions - inputPlaceName <- transition.nonProvidedIngredients.map(_.name) - } yield (inputPlaceName, transition) - - // Then obtain the places with multiple out-adjacent transitions - val ingredientsWithMultipleConsumers = placeNameWithTransition.groupBy { - case (placeName, _) => placeName - } // Group by place name - .filter { case (_, interactions) => interactions.size >= 2 } // Only keep those place names which have more than one out-adjacent transition - .map { case (placeName, interactions) => (placeName, interactions.map(_._2)) } // Cleanup the data structure - - ingredientsWithMultipleConsumers - } - - private def createPlace(label: String, placeType: PlaceType): Place = Place(label = s"${placeType.labelPrepend}$label", placeType) - - private def convertCheckpointEventToInteraction(e: CheckPointEvent) = - Interaction( - name = s"${checkpointEventInteractionPrefix}${e.name}", - inputIngredients = Seq.empty, - output = Seq(Event(e.name)), - requiredEvents = e.requiredEvents, - requiredOneOfEvents = e.requiredOneOfEvents) - - private def convertSieveToInteraction(s: Sieve) = - Interaction( - name = s"${sieveInteractionPrefix}${s.name}", - inputIngredients = s.inputIngredients, - output = s.output, - requiredEvents = Set.empty, - requiredOneOfEvents = Set.empty - ) - - private def flattenSubRecipesToInteraction(recipe: Recipe): Set[InteractionDescriptor] = { - def copyInteraction(i: InteractionDescriptor) = Interaction( - name = s"${subRecipePrefix}${recipe.name}$$${i.name}", - inputIngredients = i.inputIngredients, - output = i.output, - requiredEvents = i.requiredEvents, - requiredOneOfEvents = i.requiredOneOfEvents, - predefinedIngredients = i.predefinedIngredients, - overriddenIngredientNames = i.overriddenIngredientNames, - overriddenOutputIngredientName = i.overriddenOutputIngredientName, - maximumInteractionCount = i.maximumInteractionCount, - failureStrategy = i.failureStrategy, - eventOutputTransformers = i.eventOutputTransformers, - isReprovider = i.isReprovider, - oldName = Option(i.originalName) - ) - - recipe.interactions.map(copyInteraction).toSet ++ - recipe.sieves.map(convertSieveToInteraction) ++ - recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ - recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) - } - - private def flattenSensoryEvents(recipe: Recipe): Set[com.ing.baker.recipe.common.Event] = { - recipe.sensoryEvents ++ recipe.subRecipes.flatMap(flattenSensoryEvents) - } -} diff --git a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/package.scala b/core/baker-compiler/src/main/scala/com/ing/baker/compiler/package.scala deleted file mode 100644 index 04ea1f29f..000000000 --- a/core/baker-compiler/src/main/scala/com/ing/baker/compiler/package.scala +++ /dev/null @@ -1,129 +0,0 @@ -package com.ing.baker - -import com.ing.baker.il._ -import com.ing.baker.il.failurestrategy.InteractionFailureStrategy -import com.ing.baker.il.petrinet._ -import com.ing.baker.recipe.common -import com.ing.baker.recipe.common.InteractionDescriptor -import com.ing.baker.types._ - -import scala.collection.immutable.Seq - -package object compiler { - - def ingredientToCompiledIngredient(ingredient: common.Ingredient): IngredientDescriptor = IngredientDescriptor(ingredient.name, ingredient.ingredientType) - - def eventToCompiledEvent(event: common.Event): EventDescriptor = EventDescriptor(event.name, event.providedIngredients.map(ingredientToCompiledIngredient)) - - implicit class InteractionOps(interaction: InteractionDescriptor) { - - def toInteractionTransition(defaultFailureStrategy: common.InteractionFailureStrategy, allIngredientNames: Set[String]): InteractionTransition = - interactionTransitionOf(interaction, defaultFailureStrategy, allIngredientNames) - - def interactionTransitionOf(interactionDescriptor: InteractionDescriptor, - defaultFailureStrategy: common.InteractionFailureStrategy, - allIngredientNames: Set[String]): InteractionTransition = { - - //This transforms the event using the eventOutputTransformer to the new event - //If there is no eventOutputTransformer for the event the original event is returned - def transformEventType(event: common.Event): common.Event = - interactionDescriptor.eventOutputTransformers.get(event) - match { - case Some(eventOutputTransformer) => - new common.Event { - override val name: String = eventOutputTransformer.newEventName - override val providedIngredients: Seq[common.Ingredient] = event.providedIngredients.map(i => - new common.Ingredient(eventOutputTransformer.ingredientRenames.getOrElse(i.name, i.name), i.ingredientType)) - } - case _ => event - } - - def transformEventOutputTransformer(recipeEventOutputTransformer: common.EventOutputTransformer): EventOutputTransformer = - EventOutputTransformer(recipeEventOutputTransformer.newEventName, recipeEventOutputTransformer.ingredientRenames) - - def transformEventToCompiledEvent(event: common.Event): EventDescriptor = { - EventDescriptor( - event.name, - event.providedIngredients.map(ingredientToCompiledIngredient)) - } - - // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe - // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe - // Replace BakerEventList to BakerEventList tag as know in compiledRecipe - // Replace ingredient tags with overridden tags - val inputFields: Seq[(String, Type)] = interactionDescriptor.inputIngredients - .map { ingredient => - if (ingredient.name == common.recipeInstanceIdName) il.recipeInstanceIdName -> ingredient.ingredientType - else if(ingredient.name == common.recipeInstanceMetadataName) il.recipeInstanceMetadataName -> ingredient.ingredientType - else if(ingredient.name == common.recipeInstanceEventListName) il.recipeInstanceEventListName -> ingredient.ingredientType - else interactionDescriptor.overriddenIngredientNames.getOrElse(ingredient.name, ingredient.name) -> ingredient.ingredientType - } - - val (originalEvents, eventsToFire): (Seq[EventDescriptor], Seq[EventDescriptor]) = { - val originalCompiledEvents = interactionDescriptor.output.map(transformEventToCompiledEvent) - val compiledEvents = interactionDescriptor.output.map(transformEventType).map(transformEventToCompiledEvent) - (originalCompiledEvents, compiledEvents) - } - - //For each ingredient that is not provided - //And is of the type Optional or Option - //Add it to the predefinedIngredients List as empty - //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. - val predefinedIngredientsWithOptionalsEmpty: Map[String, Value] = - inputFields.flatMap { - case (name, types.OptionType(_)) if !allIngredientNames.contains(name) => Seq(name -> NullValue) - case _ => Seq.empty - }.toMap ++ interactionDescriptor.predefinedIngredients - - val (failureStrategy: InteractionFailureStrategy, exhaustedRetryEvent: Option[EventDescriptor], functionalRetryEvent: Option[EventDescriptor]) = { - interactionDescriptor.failureStrategy.getOrElse[common.InteractionFailureStrategy](defaultFailureStrategy) match { - case common.InteractionFailureStrategy.RetryWithIncrementalBackoff(initialTimeout, backoffFactor, maximumRetries, maxTimeBetweenRetries, fireRetryExhaustedEvent, fireFunctionalEvent) => - val exhaustedRetryEvent: Option[EventDescriptor] = fireRetryExhaustedEvent match { - case Some(None) => Some(EventDescriptor(interactionDescriptor.name + exhaustedEventAppend, Seq.empty)) - case Some(Some(eventName)) => Some(EventDescriptor(eventName, Seq.empty)) - case None => None - } - val functionalFailedEvent: Option[EventDescriptor] = fireFunctionalEvent match { - case Some(None) => Some(EventDescriptor(interactionDescriptor.name + functionalFailedEventAppend, Seq.empty)) - case Some(Some(eventName)) => Some(EventDescriptor(eventName, Seq.empty)) - case None => None - } - - (il.failurestrategy.RetryWithIncrementalBackoff(initialTimeout, backoffFactor, maximumRetries, maxTimeBetweenRetries, exhaustedRetryEvent, functionalFailedEvent),exhaustedRetryEvent, functionalFailedEvent) - - case common.InteractionFailureStrategy.BlockInteraction() => (il.failurestrategy.BlockInteraction, None, None) - - case common.InteractionFailureStrategy.FireEventAfterFailure(eventNameOption) => - val eventName = eventNameOption.getOrElse(interactionDescriptor.name + exhaustedEventAppend) - val exhaustedRetryEvent: EventDescriptor = EventDescriptor(eventName, Seq.empty) - (il.failurestrategy.FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), None) - - case common.InteractionFailureStrategy.FireEventAndBlock(eventNameOption) => - val eventName = eventNameOption.getOrElse(interactionDescriptor.name + exhaustedEventAppend) - val exhaustedRetryEvent: EventDescriptor = EventDescriptor(eventName, Seq.empty) - (il.failurestrategy.FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), None) - - case common.InteractionFailureStrategy.FireEventAndResolve(eventNameOption) => - val eventName = eventNameOption.getOrElse(interactionDescriptor.name + functionalFailedEventAppend) - val functionalFailed: EventDescriptor = EventDescriptor(eventName, Seq.empty) - (il.failurestrategy.FireFunctionalEventAfterFailure(functionalFailed), None, Some(functionalFailed)) - - case _ => (il.failurestrategy.BlockInteraction, None, None) - } - } - - InteractionTransition( - eventsToFire = eventsToFire ++ exhaustedRetryEvent ++ functionalRetryEvent, - originalEvents = originalEvents ++ exhaustedRetryEvent ++ functionalRetryEvent, - requiredIngredients = inputFields.map { case (name, ingredientType) => IngredientDescriptor(name, ingredientType) }, - interactionName = interactionDescriptor.name, - originalInteractionName = interactionDescriptor.originalName, - predefinedParameters = predefinedIngredientsWithOptionalsEmpty, - maximumInteractionCount = interactionDescriptor.maximumInteractionCount, - failureStrategy = failureStrategy, - eventOutputTransformers = interactionDescriptor.eventOutputTransformers.map { - case (event, transformer) => event.name -> transformEventOutputTransformer(transformer) }, - isReprovider = interactionDescriptor.isReprovider) - } - } -} diff --git a/core/baker-compiler/src/test/java/com/ing/baker/compiler/RecipeCompilerTests.java b/core/baker-compiler/src/test/java/com/ing/baker/compiler/RecipeCompilerTests.java deleted file mode 100644 index 5c453adc5..000000000 --- a/core/baker-compiler/src/test/java/com/ing/baker/compiler/RecipeCompilerTests.java +++ /dev/null @@ -1,225 +0,0 @@ -package com.ing.baker.compiler; - -import com.ing.baker.il.CompiledRecipe; -import com.ing.baker.il.EventDescriptor; -import com.ing.baker.il.petrinet.InteractionTransition; -import com.ing.baker.recipe.annotations.FiresEvent; -import com.ing.baker.recipe.annotations.RequiresIngredient; -import com.ing.baker.recipe.javadsl.CheckPointEvent; -import com.ing.baker.recipe.javadsl.Interaction; -import com.ing.baker.recipe.javadsl.InteractionDescriptor; -import com.ing.baker.recipe.javadsl.Recipe; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class RecipeCompilerTests { - - static class EventA { - } - - static class EventB { - } - - static class EventC { - } - - static class EventD { - } - - static class EventE { - } - - public interface InteractionA extends Interaction { - - interface ReserveItemsOutcome { - } - - class OrderHadUnavailableItems implements ReserveItemsOutcome { - - public final List unavailableItems; - - public OrderHadUnavailableItems(List unavailableItems) { - this.unavailableItems = unavailableItems; - } - } - - class ItemsReserved implements ReserveItemsOutcome { - - public final List reservedItems; - - public ItemsReserved(List reservedItems) { - this.reservedItems = reservedItems; - } - } - - @FiresEvent(oneOf = {ItemsReserved.class, OrderHadUnavailableItems.class}) - ReserveItemsOutcome apply( - @RequiresIngredient("orderId") String id, - @RequiresIngredient("items") List items - ); - } - - public interface InteractionB extends Interaction { - - interface ReserveItemsOutcome { - } - - class OrderHadUnavailableItems implements ReserveItemsOutcome { - - public final List unavailableItems; - - public OrderHadUnavailableItems(List unavailableItems) { - this.unavailableItems = unavailableItems; - } - } - - class ItemsReserved implements ReserveItemsOutcome { - - public final List reservedItems; - - public ItemsReserved(List reservedItems) { - this.reservedItems = reservedItems; - } - } - - @FiresEvent(oneOf = {ItemsReserved.class, OrderHadUnavailableItems.class}) - ReserveItemsOutcome apply( - @RequiresIngredient("orderId") String id, - @RequiresIngredient("items") List items - ); - } - - interface ReserveItemsOutcome { - } - - @Test - public void shouldCompileSimpleRecipe() { - Recipe recipe = new Recipe("recipe") - .withSensoryEvents(EventA.class); - CompiledRecipe compiled = RecipeCompiler.compileRecipe(recipe); - - Assertions.assertEquals("8682aa9310e38509", compiled.recipeId()); - - } - - @Test - public void shouldCompileSimpleRecipeWithInteraction() { - - Recipe recipe = new Recipe("recipe") - .withSensoryEvents(EventA.class) - .withInteraction(InteractionDescriptor.of(InteractionA.class)); - CompiledRecipe compiled = RecipeCompiler.compileRecipe(recipe); - - Assertions.assertEquals("796a3cb3eb68b35d", compiled.recipeId()); - - } - - @Test - public void shouldFailOnDuplicatedEventsEvenWithFiringLimitDifference() { - // "Duplicated" EventA - // Because Set backing sensoryEvents has <=4 elements (is a specialized Set.Set4) it only uses `equals` in `contains` - Recipe recipe1 = new Recipe("recipe1") - .withSensoryEvents(EventA.class, EventB.class, EventC.class, EventD.class) - .withSensoryEventsNoFiringLimit(EventA.class); - - // "Duplicated" EventA - // Because Set backing sensoryEvents has >4 elements (is a HashSet) and uses `hashcode` in `contains` - Recipe recipe2 = new Recipe("recipe2") - .withSensoryEvents(EventA.class, EventB.class, EventC.class, EventD.class, EventE.class) - .withSensoryEventsNoFiringLimit(EventA.class); - - Assertions.assertThrows(RecipeValidationException.class, () -> - RecipeCompiler.compileRecipe(recipe1)); - - Assertions.assertThrows(RecipeValidationException.class, () -> - RecipeCompiler.compileRecipe(recipe2)); - } - - @Test - public void shouldAddInteractionsForCheckpointEvents() { - - Recipe recipe = new Recipe("recipe1") - .withSensoryEvents(EventB.class, EventC.class, EventD.class) - .withSensoryEventsNoFiringLimit(EventA.class) - .withCheckpointEvent(new CheckPointEvent("Success") - .withRequiredEvents(EventB.class, EventC.class)); - - - CompiledRecipe compiled = RecipeCompiler.compileRecipe(recipe); - - Object actual = convertList(compiled.petriNet().transitions()) - .stream() - .filter(InteractionTransition.class::isInstance) - .map(i -> ((InteractionTransition) i).interactionName()) - .collect(Collectors.toUnmodifiableList()); - - Assertions.assertEquals(java.util.List.of("$CheckpointEventInteraction$Success"), actual); - - } - - class SubSubSubEvent { - } - - class SubSubEvent { - } - - class SubEvent { - } - - class Event { - } - - @Test - public void shouldAddSubRecipe() { - // Several layers deep to verify if everything propagates correctly - Recipe subSubSubRecipe = new Recipe("subSubSubRecipe") - .withSensoryEvent(SubSubSubEvent.class) - .withCheckpointEvent(new CheckPointEvent("subSubSubCheckpointEvent")) - .withInteraction(InteractionDescriptor.of(InteractionA.class)); - - Recipe subSubRecipe = new Recipe("SubSubRecipe") - .withSensoryEvent(SubSubEvent.class) - .withSubRecipe(subSubSubRecipe) - .withCheckpointEvent(new CheckPointEvent("subSubCheckpointEvent")) - .withInteraction(InteractionDescriptor.of(InteractionB.class)); - - Recipe subRecipe = new Recipe("SubRecipe") - .withSensoryEvent(SubEvent.class) - .withSubRecipe(subSubRecipe) - .withCheckpointEvent(new CheckPointEvent("subCheckpointEvent")) - .withInteraction(InteractionDescriptor.of(InteractionA.class)); - - Recipe recipe = new Recipe("recipe") - .withSensoryEvent(Event.class) - .withSubRecipe(subRecipe) - .withCheckpointEvent(new CheckPointEvent("checkpointEvent")); - - - CompiledRecipe compiled = RecipeCompiler.compileRecipe(recipe); - - Object actualSensoryEvents = convertList(compiled.sensoryEvents()).stream().map(i -> ((EventDescriptor) i).name()).collect(Collectors.toUnmodifiableList()); - List expectedSensoryEvents = java.util.List.of("SubSubEvent", "SubEvent", "SubSubSubEvent", "Event"); - - Assertions.assertEquals(expectedSensoryEvents, actualSensoryEvents); - - Object actual = convertList(compiled.petriNet().transitions()) - .stream() - .filter(InteractionTransition.class::isInstance) - .map(i -> ((InteractionTransition) i).interactionName()) - .collect(Collectors.toUnmodifiableList()); - - List expected = java.util.List.of("$CheckpointEventInteraction$subSubSubCheckpointEvent", "$SubRecipe$SubRecipe$InteractionA", "$CheckpointEventInteraction$subSubCheckpointEvent", "$CheckpointEventInteraction$subCheckpointEvent", "$CheckpointEventInteraction$checkpointEvent", "$SubRecipe$subSubSubRecipe$InteractionA", "$SubRecipe$SubSubRecipe$InteractionB"); - - Assertions.assertEquals(expected, actual); - } - - private java.util.List convertList(scala.collection.immutable.Set list) { - java.util.List l = new ArrayList(); - list.foreach(i -> l.add(i)); - return l; - } -} diff --git a/core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt b/core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt deleted file mode 100644 index 081474cbb..000000000 --- a/core/baker-compiler/src/test/kotlin/RecipeCompilerSpecTest.kt +++ /dev/null @@ -1,644 +0,0 @@ -package com.ing.baker.compiler - -import com.ing.baker.il.* -import com.ing.baker.il.RecipeVisualStyle.* -import com.ing.baker.il.petrinet.InteractionTransition -import com.ing.baker.recipe.TestRecipe.getRecipe -import com.ing.baker.recipe.TestRecipe.* -import com.ing.baker.recipe.TestRecipeJava -import com.ing.baker.recipe.common.EventOutputTransformer -import com.ing.baker.recipe.common.Ingredient -import com.ing.baker.recipe.common.InteractionDescriptor -import com.ing.baker.recipe.scaladsl.* -import com.ing.baker.types.* -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.jupiter.api.fail -import scala.* -import scala.collection.immutable.Seq -import scala.concurrent.duration.Duration -import java.util.* -import java.util.concurrent.TimeUnit -import kotlin.reflect.javaType -import kotlin.reflect.typeOf -import com.ing.baker.compiler.ScalaConversions.asScala -import com.ing.baker.compiler.ScalaConversions.asJava - - -class RecipeCompilerScalaTest { - @Test - fun `The recipe compiler should not have validation errors for a valid recipe`() { - val recipe: Recipe = getRecipe("ValidRecipe") - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertEquals(emptyList(), compiledRecipe.validationErrors) - - // dumpToFile("TestRecipe.svg", compiledRecipe.getVisualRecipeAsSVG) - } - - @Test - fun `The recipe compiler should add the exhausted retry event to the interaction event output list if defined`() { - val exhaustedEvent = event("RetryExhausted", emptyList()) - val recipe = recipe("RetryExhaustedRecipe") - .withSensoryEvent(initialEvent) - .withInteractions(interactionOne.withFailureStrategy( - InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10.milliseconds) - .withUntil(Some(UntilDeadline(10.seconds))) - .withFireRetryExhaustedEvent(exhaustedEvent) - .build())) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - - assertTrue(exhaustedEvent.name() in compiledRecipe.allEvents.map(EventDescriptor::name)) - } - - @Test - fun `The recipe compiler should generate the same id for the same recipe`() { - val first = RecipeCompiler.compileRecipe(getRecipe("ValidRecipe")).recipeId() - (1.. 10) - .map { getRecipe("ValidRecipe")} - .map{ RecipeCompiler.compileRecipe(it).recipeId() } - .forEach { - assertEquals(first, it) - } - } - - @Test - fun `The recipe compiler should generate different ids for recipes with changes on transitions other than the name`() { - val input = ingredient("ingredient") - val output = event("event", emptyList()) - val interaction = interaction("interaction", listOf(input), listOf(output)) - val name = "RecipeName" - val recipe1 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 1)) - val recipe2 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 2)) - assertTrue(RecipeCompiler.compileRecipe(recipe1).recipeId() != RecipeCompiler.compileRecipe(recipe2).recipeId()) - } - - @Test - fun `The recipe compiler should give a list of missing ingredients if an interaction has an ingredient that is not provided by any other event or interaction`() { - val recipe = recipe("NonProvidedIngredient") - .withSensoryEvent(secondEvent) - .withInteractions(interactionOne) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue("Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give an error if the RecipeInstanceId is required and is not of the String type`() { - val wrongrecipeInstanceIdInteraction = - interaction( - name = "wrongrecipeInstanceIdInteraction", - inputIngredients = listOf(ingredient(common.recipeInstanceIdName), initialIngredient), - output = emptyList() - ) - - val recipe = recipe("NonProvidedIngredient") - .withSensoryEvent(initialEvent) - .withInteractions(wrongrecipeInstanceIdInteraction) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue("Non supported process id type: Int32 on interaction: 'wrongrecipeInstanceIdInteraction'" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give an error if the MetaData is required and is not of the Map String to String type`() { - val wrongMetaDataInteraction = - interaction( - name = "wrongMetaDataInteraction", - inputIngredients = listOf(ingredient(common.recipeInstanceMetadataName), initialIngredient), - output = emptyList() - ) - - val recipe = recipe("NonProvidedIngredient") - .withSensoryEvent(initialEvent) - .withInteractions(wrongMetaDataInteraction) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue("Non supported MetaData type: Int32 on interaction: 'wrongMetaDataInteraction'" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give an error if the baker internal ingredients are provided`() { - val wrongDateEvent = event("WrongDataEvent", - listOf( - ingredient("recipeInstanceId"), - ingredient("RecipeInstanceMetaData")), - maxFiringLimit = null) - - val wrongDateEvent2 = event("WrongDataEvent2", - listOf( - ingredient("RecipeInstanceEventList")), - maxFiringLimit = null) - - val wrongMetaDataInteraction = - interaction( - name = "wrongDataProvidedInteraction", - inputIngredients = listOf(ingredient(common.recipeInstanceIdName), initialIngredient), - output = listOf(wrongDateEvent2) - ) - - val recipe = recipe("WrongDataRecipe") - .withSensoryEvents(initialEvent, wrongDateEvent) - .withInteractions(wrongMetaDataInteraction) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertEquals(listOf( - "Ingredient 'RecipeInstanceMetaData' is provided and this is a reserved name for internal use in Baker", - "Ingredient 'recipeInstanceId' is provided and this is a reserved name for internal use in Baker", - "Ingredient 'RecipeInstanceEventList' is provided and this is a reserved name for internal use in Baker"), - compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give a list of wrong ingredients when an ingredient is of the wrong type`() { - val initialIngredientInt = Ingredient("initialIngredient", recordType(listOf( - RecordField("data", Int32) - ))) - val initialEventInt = event("InitialEvent", listOf(initialIngredientInt), null) - - val recipe = recipe("WrongTypedIngredient") - .withInteractions( - interactionOne) - .withSensoryEvent(initialEventInt) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue("Interaction 'InteractionOne' expects ingredient 'initialIngredient:CharArray', however incompatible type: 'Record(data: Int32)' was provided" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give a list of wrong ingredients when an optional ingredient is of the wrong Option type`() { - val initialIngredientOptionalInt = ingredient>("initialIngredientOptionalInt") - val initialIngredientOptionalString = ingredient>("initialIngredientOptionalInt") - val initialIngredientOptionInt = ingredient>>("initialIngredientOptionInt") - val initialIngredientOptionString = ingredient>>("initialIngredientOptionInt") - val initialEventIntOptional = event("initialEventIntOptional", listOf(initialIngredientOptionalString), null) - val initialEventIntOption = event("initialEventIntOption", listOf(initialIngredientOptionString), null) - val interactionOptional = - interaction( - name = "InteractionWithOptional", - inputIngredients = listOf(recipeInstanceId, initialIngredientOptionalInt, initialIngredientOptionInt), - output = emptyList() - ) - - val recipe = recipe("WrongTypedOptionalIngredient") - .withInteractions( - interactionOptional) - .withSensoryEvents(initialEventIntOptional, initialEventIntOption) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionalInt:Option[Int32]', however incompatible type: 'Option[CharArray]' was provided" in compiledRecipe.validationErrors) - assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionInt:Option[List[Int32]]', however incompatible type: 'Option[List[CharArray]]' was provided" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give no errors if an Optional ingredient is of the correct Option type`(){ - val initialIngredientInt = ingredient>>("initialIngredient") - val initialEventInt = event("InitialEvent", listOf(initialIngredientInt), null) - val interactionOptional = - interaction( - name = "InteractionWithOptional", - inputIngredients = listOf(recipeInstanceId, initialIngredientInt), - output = emptyList() - ) - - val recipe = recipe("CorrectTypedOptionalIngredient") - .withInteractions( - interactionOptional) - .withSensoryEvent(initialEventInt) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue(compiledRecipe.validationErrors.isEmpty()) - } - - @Test - fun `The recipe compiler should give a list of wrong ingredients if a predefined ingredient is of the wrong type`() { - val recipe = recipe("WrongGivenPredefinedIngredient") - .withInteractions( - interactionOne - .withRequiredEvent(initialEvent) - .withPredefinedIngredients("initialIngredient" to Integer.valueOf(12))) - .withSensoryEvent(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue("Predefined argument 'initialIngredient' is not of type: CharArray on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give a list of wrong ingredients if a predefined ingredient is not needed by the interaction`() { - val recipe = recipe("WrongGivenIngredient") - .withInteractions( - interactionOne - .withPredefinedIngredients("WrongIngredient" to null)) - .withSensoryEvent(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue("Predefined argument 'WrongIngredient' is not defined on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should validate if unreachable interactions exist or not`() { - val recipe = recipe("RecipeWithUnreachableInteraction") - .withInteractions(interactionSeven.withMaximumInteractionCount(1), interactionEight) - .withSensoryEvent(initialEvent) - - val compiledRecipe = RecipeCompiler.compileRecipe(recipe, - validationSettings(allowNonExecutableInteractions = false) - ) - - assertTrue("InteractionEight is not executable" in compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should fail compilation when the interaction name is null or empty`() { - listOf("", null).forEach { name -> - val invalidInteraction = interaction(name, emptyList(), emptyList()) - val recipe = recipe("InteractionNameTest").withInteractions(invalidInteraction).withSensoryEvent(initialEvent) - - assertFailsWith(exceptionMessage = "Interaction with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) - } - } - } - - @Test - fun `The recipe compiler should fail compilation when the event name is null or empty`() { - listOf("", null).forEach { name -> - val invalidEvent = event(name, emptyList()) - val recipe = recipe("EventNameTest").withSensoryEvent(invalidEvent).withInteraction(interactionOne) - - assertFailsWith(exceptionMessage = "Event with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) - } - } - } - - @Test - fun `The recipe compiler should fail compilation when the ingredient name is null or empty`() { - listOf("", null).forEach { name -> - val invalidIngredient = ingredient(name) - val recipe = recipe("IngredientNameTest").withSensoryEvent(event("someEvent", listOf(invalidIngredient))).withInteraction(interactionOne) - - assertFailsWith(exceptionMessage = "Ingredient with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) - } - } - } - - @Test - fun `The recipe compiler should fail compilation when the recipe name is null or empty`() { - listOf("", null).forEach { name -> - val recipe = recipe(name) - - assertFailsWith(exceptionMessage = "Recipe with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) - } - } - } - - @Test - //TODO Change name of the test. compiler is not failing - fun `The recipe compiler should fail compilation when an Interaction is reprovider, but has no required events`() { - val recipe: Recipe = recipe("LoopingWithReprovider") - .withInteraction(interactionOne.isReprovider(true)) - .withSensoryEvents(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertEquals(listOf("Reprovider interaction InteractionOne needs to have a event precondition"), compiledRecipe.validationErrors) - } - - @Test - fun `The recipe compiler should give the interaction an optional ingredient as empty the ingredient is not provided`() { - val recipe: Recipe = recipe("MissingOptionalRecipe") - .withInteraction(interactionWithOptionalIngredients) - .withSensoryEvent(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - - assertTrue(compiledRecipe.validationErrors.isEmpty()) - compiledRecipe.interactionTransitions().foreach { transition -> - if (transition.interactionName().equals("OptionalIngredientInteraction")) { - assertEquals(4, transition.predefinedParameters().size()) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) - } - } - } - - @Test - fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is provided`() { - val optionalProviderEvent = event("optionalProviderEvent", listOf(missingJavaOptional)) - - val recipe: Recipe = recipe("MissingOptionalRecipe") - .withInteraction(interactionWithOptionalIngredients) - .withSensoryEvents(initialEvent, optionalProviderEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - - assertTrue(compiledRecipe.validationErrors.isEmpty()) - compiledRecipe.interactionTransitions().foreach { transition -> - if (transition.interactionName().equals("OptionalIngredientInteraction")) { - assertEquals(3, transition.predefinedParameters().size()) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) - } - } - } - - @Test - fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is renamed and provided via an event`() { - val stringOptionIngredient = ingredient>("stringOptionIngredient") - val renamedStringOptionIngredient = ingredient>("renamedStringOptionIngredient") - - val eventWithOptionIngredient = event("eventWithOptionIngredient", listOf(stringOptionIngredient)) - - val interactionWithOptionIngredient = interaction("interactionWithOptionIngredient", listOf(initialIngredient), listOf(eventWithOptionIngredient)) - - val secondInteraction = interaction("secondInteraction", listOf(renamedStringOptionIngredient), emptyList()) - - val recipe = recipe("interactionWithEventOutputTransformer") - .withSensoryEvent(initialEvent) - .withInteraction(interactionWithOptionIngredient - .withEventOutputTransformer(eventWithOptionIngredient, "RenamedEventWithOptionIngredient", mapOf("stringOptionIngredient" to "renamedStringOptionIngredient").asScala)) - .withInteraction(secondInteraction) - - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertTrue(compiledRecipe.validationErrors.isEmpty()) - - val transition = compiledRecipe.interactionTransitions().find{ it.interactionName() == "secondInteraction" }.get() - assertTrue("renamedStringOptionIngredient" in transition.nonProvidedIngredients().asJava.map(IngredientDescriptor::name)) - } - - @Test - fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is predefined`() { - val ingredientValue = Optional.of("value") - val recipe: Recipe = recipe("MissingOptionalRecipe") - .withInteraction( - interactionWithOptionalIngredients - .withPredefinedIngredients("missingJavaOptional" to ingredientValue) - ) - .withSensoryEvents(initialEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - - assertTrue(compiledRecipe.validationErrors.isEmpty()) - compiledRecipe.interactionTransitions().foreach { transition -> - if (transition.interactionName().equals("OptionalIngredientInteraction")) { - assertEquals(4, transition.predefinedParameters().size()) - assertEquals(Some(PrimitiveValue("value")), transition.predefinedParameters()["missingJavaOptional"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) - } - } - } - - @Test - fun `The recipe compiler should give the interaction an optional ingredient with value when the ingredient is provided, but not wrapped in an Option type`() { - val optionalProviderEvent = event("optionalProviderEvent", listOf(missingJavaOptionalDirectString)) - - val recipe: Recipe = recipe("MissingOptionalRecipe") - .withInteraction(interactionWithOptionalIngredients) - .withSensoryEvents(initialEvent, optionalProviderEvent) - - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - - assertTrue(compiledRecipe.validationErrors.isEmpty()) - compiledRecipe.interactionTransitions().foreach { transition -> - if (transition.interactionName().equals("OptionalIngredientInteraction")) { - assertEquals(3, transition.predefinedParameters().size()) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingJavaOptional2"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption"]) - assertEquals(Some(NullValue), transition.predefinedParameters()["missingScalaOption2"]) - } - } - } - - @Test - fun `The recipe compiler should give the correct id when it compiles a java recipe`() { - val recipe = TestRecipeJava.getRecipe("id-test-recipe") - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertEquals("220827c42a75b3f8", compiledRecipe.recipeId()) - } - - @Test - fun `The recipe compiler should give the interaction with Reprovider enabled when it compiles a java recipe and changes recipeId`() { - val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertEquals("416e8abc02abcbee", compiledRecipe.recipeId()) - } - - @Test - fun `The recipe compiler should give the interaction for checkpoint-events when it compiles a java recipe`() { - val recipe = TestRecipeJava.getRecipe("id-test-recipe") - .withCheckpointEvent( - checkPointEvent("Success") - .withRequiredEvent(initialEvent) - ) - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - assertEquals("469441173f91869a", compiledRecipe.recipeId()) - assertEquals(1, compiledRecipe.petriNet().transitions().count { it is InteractionTransition && it.interactionName() - .contains("${checkpointEventInteractionPrefix}Success") }) - } - - @Test - fun `give the interaction for sub-recipes when it compiles a java recipe`() { - - val subSubRecipe: Recipe = recipe("SubSubRecipe") - .withInteractions( - interactionOne - .withEventOutputTransformer(interactionOneSuccessful, mapOf("interactionOneOriginalIngredient" to "interactionOneIngredient").asScala) - .withFailureStrategy(InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() - .withInitialDelay(10.milliseconds) - .withUntil(Option.apply(UntilMaximumRetries(3))) - .build()), - interactionTwo - .withOverriddenIngredientName("initialIngredientOld", "initialIngredient"), - ) - - val subRecipe: Recipe = recipe("SubRecipe") - .withInteractions( - interactionThree - .withMaximumInteractionCount(1), - interactionFour - .withRequiredEvents(secondEvent, eventFromInteractionTwo), - interactionFive, - interactionSix, - ) - .withSubRecipe(subSubRecipe) - - val recipe: Recipe = recipe("Recipe") - .withSensoryEvents( - initialEvent, - initialEventExtendedName, - secondEvent, - notUsedSensoryEvent - ) - .withInteractions( - providesNothingInteraction, - sieveInteraction - ) - .withSubRecipe(subRecipe) - - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) - - val res = compiledRecipe.petriNet().transitions().asJava - .flatMap { when(it) { - is InteractionTransition -> listOf(it.interactionName()) - else -> emptyList() - }} - .filter{ it.startsWith(subRecipePrefix) }.toSet() - - assertEquals("ae2282f55f0a4f9f", compiledRecipe.recipeId()) - assertEquals(setOf( - "\$SubRecipe\$SubSubRecipe\$InteractionOne", - "\$SubRecipe\$SubSubRecipe\$InteractionTwo", - "\$SubRecipe\$SubRecipe\$InteractionThree", - "\$SubRecipe\$SubRecipe\$InteractionFour", - "\$SubRecipe\$SubRecipe\$InteractionFive", - "\$SubRecipe\$SubRecipe\$InteractionSix", - ), res) - - val vis = RecipeVisualizer.visualizeRecipe(compiledRecipe, `RecipeVisualStyle$`.`MODULE$`.default(), emptySet().asScala, emptySet().asScala, emptySet().asScala) - - println(vis) - } - - - - - private inline fun assertFailsWith(exceptionMessage: String, message: String? = null, init: () -> Any?) { - try { - init() - } catch (t: Throwable) { - if (t is T) { - if (t.message == exceptionMessage) - return - else - fail(message ?: "Exception ${T::class.simpleName} thrown but message is different") - } - fail(message ?: "Exception thrown but not ${T::class.simpleName}") - } - fail(message ?: "No exception thrown") - } - - - private fun validationSettings(allowCycles: Boolean = true, allowDisconnectedness: Boolean = true, allowNonExecutableInteractions: Boolean = true) = - ValidationSettings(allowCycles, allowDisconnectedness, allowNonExecutableInteractions) - - - private fun recordType(recordsFields: List) = RecordType(recordsFields.asScala) - - - private fun recipe(name: String?): Recipe = Recipe.apply(name) - private fun event(name: String?, ingredients: List, maxFiringLimit: Int? = null) = Event.apply(name, ingredients.asScala).let { - when(maxFiringLimit) { - null -> it - else -> it.withMaxFiringLimit(maxFiringLimit) - } - } - - - - // Scala glue - val initialEvent = initialEvent() - val secondEvent = secondEvent() - val interactionOneSuccessful = interactionOneSuccessful() - val eventFromInteractionTwo = eventFromInteractionTwo() - val initialEventExtendedName = initialEventExtendedName() - val notUsedSensoryEvent = notUsedSensoryEvent() - - val interactionOne = interactionOne() - val interactionTwo = interactionTwo() - val interactionThree = interactionThree() - val interactionFour = interactionFour() - val interactionFive = interactionFive() - val interactionSix = interactionSix() - val interactionSeven = interactionSeven() - val interactionEight = interactionEight() - val interactionWithOptionalIngredients = interactionWithOptionalIngredients() - val providesNothingInteraction = providesNothingInteraction() - val sieveInteraction = sieveInteraction() - - val initialIngredient = initialIngredient() - val missingJavaOptional = missingJavaOptional() - val missingJavaOptionalDirectString = missingJavaOptionalDirectString() - - val recipeInstanceId = ingredient(common.recipeInstanceIdName) - - fun Recipe.withInteractions(vararg newInteractions: InteractionDescriptor) = this.withInteractions(newInteractions.asScala as Seq?) - fun Recipe.withSensoryEvents(vararg sensoryEvent: com.ing.baker.recipe.common.Event) = this.withSensoryEvents(sensoryEvent.asScala as Seq) - val Int.milliseconds get() = Duration.create(this.toLong(), TimeUnit.MILLISECONDS) - val Int.seconds get() = Duration.create(this.toLong(), TimeUnit.SECONDS) - - - object common { - val recipeInstanceIdName = com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() - val recipeInstanceMetadataName = com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() - } - - @OptIn(ExperimentalStdlibApi::class) - inline fun ingredient(name: String?) = Ingredient(name, Converters.readJavaType(typeOf().javaType)) - - private fun checkPointEvent(name: String): CheckPointEvent = - `CheckPointEvent$`.`MODULE$`.apply(name) - - private fun interaction( - name: String?, - inputIngredients: List, - output: List, - requiredEvents: Set = emptySet(), - requiredOneOfEvents: Set> = emptySet(), - predefinedIngredients: Map = emptyMap(), - overriddenIngredientNames: Map = emptyMap(), - overriddenOutputIngredientName: String? = null, - maximumInteractionCount: Int? = null, - failureStrategy: com.ing.baker.recipe.common.InteractionFailureStrategy? = null, - eventOutputTransformers: Map = emptyMap(), - isReprovider: Boolean = false, - oldName: String? = null, - ): Interaction = - `Interaction$`.`MODULE$`.apply( - name, - inputIngredients.asScala, - output.asScala, - requiredEvents.asScala, - requiredOneOfEvents.map { it: Set -> it.asScala}.toSet().asScala, - predefinedIngredients.asScala, - overriddenIngredientNames.asScala, - Option.apply(overriddenOutputIngredientName), - Option.apply(maximumInteractionCount), - Option.apply(failureStrategy), - eventOutputTransformers.asScala, - isReprovider, - Option.apply(oldName) - ) - - // Ugly Scala glue - val Int32 = `Int32$`.`MODULE$` - val NullValue = `NullValue$`.`MODULE$` - val checkpointEventInteractionPrefix = com.ing.baker.il.`package$`.`MODULE$`.checkpointEventInteractionPrefix() - val subRecipePrefix = com.ing.baker.il.`package$`.`MODULE$`.subRecipePrefix() - - fun Interaction.withPredefinedIngredients(vararg ingredients: Pair) = this.withPredefinedIngredients(ingredients.map { Tuple2(it.first, it.second) }.asScala) - fun Interaction.withRequiredEvents(vararg events: com.ing.baker.recipe.common.Event) = this.withRequiredEvents(events.toList().asScala) - - object InteractionFailureStrategy { - object RetryWithIncrementalBackoff { - fun builder() = com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$builder$`.`MODULE$`.apply() - } - } - - object UntilDeadline { - operator fun invoke(duration: Duration) = com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$UntilDeadline$`.`MODULE$`.apply(duration) - - } - object UntilMaximumRetries { - operator fun invoke(maxRetries: Int) = com.ing.baker.recipe.common.`InteractionFailureStrategy$RetryWithIncrementalBackoff$UntilMaximumRetries$`.`MODULE$`.apply(maxRetries) - } - -} \ No newline at end of file diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt new file mode 100644 index 000000000..0b2707e6e --- /dev/null +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt @@ -0,0 +1,195 @@ +package com.ing.baker.compiler + +import com.ing.baker.il.petrinet.InteractionTransition +import com.ing.baker.recipe.annotations.FiresEvent +import com.ing.baker.recipe.annotations.RequiresIngredient +import com.ing.baker.recipe.javadsl.CheckPointEvent +import com.ing.baker.recipe.javadsl.Interaction +import com.ing.baker.recipe.javadsl.InteractionDescriptor +import com.ing.baker.recipe.javadsl.Recipe +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class RecipeCompilerBasicTest { + + class EventA + + class EventB + + class EventC + + class EventD + + class EventE + + interface InteractionA : Interaction { + + interface ReserveItemsOutcome + + class OrderHadUnavailableItems(val unavailableItems: List) : ReserveItemsOutcome + + class ItemsReserved(val reservedItems: List) : ReserveItemsOutcome + + @FiresEvent(oneOf = [ItemsReserved::class, OrderHadUnavailableItems::class]) + fun apply( + @RequiresIngredient("orderId") id: String, + @RequiresIngredient("items") items: List + ): ReserveItemsOutcome + } + + interface InteractionB : Interaction { + + interface ReserveItemsOutcome + + class OrderHadUnavailableItems(val unavailableItems: List) : ReserveItemsOutcome + + class ItemsReserved(val reservedItems: List) : ReserveItemsOutcome + + @FiresEvent(oneOf = [ItemsReserved::class, OrderHadUnavailableItems::class]) + fun apply( + @RequiresIngredient("orderId") id: String, + @RequiresIngredient("items") items: List + ): ReserveItemsOutcome + } + + interface ReserveItemsOutcome + + @Test + fun shouldCompileSimpleRecipe() { + val recipe = Recipe("recipe") + .withSensoryEvents(EventA::class.java) + val compiled = RecipeCompiler.compileRecipe(recipe) + + Assertions.assertEquals("8682aa9310e38509", compiled.recipeId()) + } + + @Test + fun shouldCompileSimpleRecipeWithInteraction() { + val recipe = Recipe("recipe") + .withSensoryEvents(EventA::class.java) + .withInteraction(InteractionDescriptor.of(InteractionA::class.java)) + val compiled = RecipeCompiler.compileRecipe(recipe) + + Assertions.assertEquals("796a3cb3eb68b35d", compiled.recipeId()) + } + + @Test + fun shouldFailOnDuplicatedEventsEvenWithFiringLimitDifference() { + // "Duplicated" EventA + // Because Set backing sensoryEvents has <=4 elements (is a specialized Set.Set4) it only uses `equals` in `contains` + val recipe1 = Recipe("recipe1") + .withSensoryEvents(EventA::class.java, EventB::class.java, EventC::class.java, EventD::class.java) + .withSensoryEventsNoFiringLimit(EventA::class.java) + + // "Duplicated" EventA + // Because Set backing sensoryEvents has >4 elements (is a HashSet) and uses `hashcode` in `contains` + val recipe2 = Recipe("recipe2") + .withSensoryEvents( + EventA::class.java, + EventB::class.java, + EventC::class.java, + EventD::class.java, + EventE::class.java + ) + .withSensoryEventsNoFiringLimit(EventA::class.java) + + Assertions.assertThrows(RecipeValidationException::class.java) { + RecipeCompiler.compileRecipe(recipe1) + } + + Assertions.assertThrows(RecipeValidationException::class.java) { + RecipeCompiler.compileRecipe(recipe2) + } + } + + @Test + fun shouldAddInteractionsForCheckpointEvents() { + val recipe = Recipe("recipe1") + .withSensoryEvents(EventB::class.java, EventC::class.java, EventD::class.java) + .withSensoryEventsNoFiringLimit(EventA::class.java) + .withCheckpointEvent( + CheckPointEvent("Success") + .withRequiredEvents(EventB::class.java, EventC::class.java) + ) + + val compiled = RecipeCompiler.compileRecipe(recipe) + + val actual = convertList(compiled.petriNet().transitions()) + .filterIsInstance() + .map { it.interactionName() } + .toList() + + Assertions.assertEquals(listOf("\$CheckpointEventInteraction\$Success"), actual) + } + + class SubSubSubEvent + + class SubSubEvent + + class SubEvent + + class Event + + @Test + fun shouldAddSubRecipe() { + // Several layers deep to verify if everything propagates correctly + val subSubSubRecipe = Recipe("subSubSubRecipe") + .withSensoryEvent(SubSubSubEvent::class.java) + .withCheckpointEvent(CheckPointEvent("subSubSubCheckpointEvent")) + .withInteraction(InteractionDescriptor.of(InteractionA::class.java)) + + val subSubRecipe = Recipe("SubSubRecipe") + .withSensoryEvent(SubSubEvent::class.java) + .withSubRecipe(subSubSubRecipe) + .withCheckpointEvent(CheckPointEvent("subSubCheckpointEvent")) + .withInteraction(InteractionDescriptor.of(InteractionB::class.java)) + + val subRecipe = Recipe("SubRecipe") + .withSensoryEvent(SubEvent::class.java) + .withSubRecipe(subSubRecipe) + .withCheckpointEvent(CheckPointEvent("subCheckpointEvent")) + .withInteraction(InteractionDescriptor.of(InteractionA::class.java)) + + val recipe = Recipe("recipe") + .withSensoryEvent(Event::class.java) + .withSubRecipe(subRecipe) + .withCheckpointEvent(CheckPointEvent("checkpointEvent")) + + val compiled = RecipeCompiler.compileRecipe(recipe) + + val actualSensoryEvents = convertList(compiled.sensoryEvents()) + .map { (it as com.ing.baker.il.EventDescriptor).name() } + .toList() + + val expectedSensoryEvents = listOf("SubSubEvent", "SubEvent", "SubSubSubEvent", "Event") + + Assertions.assertEquals(expectedSensoryEvents, actualSensoryEvents) + + val actual = convertList(compiled.petriNet().transitions()) + .filterIsInstance() + .map { it.interactionName() } + .toList() + + val expected = listOf( + $$"$CheckpointEventInteraction$subSubSubCheckpointEvent", + $$"$SubRecipe$SubRecipe$InteractionA", + $$"$CheckpointEventInteraction$subSubCheckpointEvent", + $$"$CheckpointEventInteraction$subCheckpointEvent", + $$"$CheckpointEventInteraction$checkpointEvent", + $$"$SubRecipe$subSubSubRecipe$InteractionA", + $$"$SubRecipe$SubSubRecipe$InteractionB" + ) + + Assertions.assertEquals(expected, actual) + } + + private fun convertList(list: scala.collection.immutable.Set<*>): List { + val result = mutableListOf() + list.foreach { item -> + result.add(item) + item + } + return result + } +} + diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt similarity index 90% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerTest.kt rename to core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt index 895edf875..3ab8818d0 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt @@ -1,11 +1,12 @@ package com.ing.baker.compiler import com.ing.baker.recipe.javadsl.Interaction +import com.ing.baker.recipe.kotlindsl.ExperimentalDsl import com.ing.baker.recipe.kotlindsl.recipe import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -class RecipeCompilerTest { +class RecipeCompilerDslTest { class EventA @@ -21,6 +22,7 @@ class RecipeCompilerTest { } @Test + @OptIn(ExperimentalDsl::class) fun `should compile dsl to recipe`() { val recipe = recipe("recipe") { @@ -37,6 +39,7 @@ class RecipeCompilerTest { @Test + @OptIn(ExperimentalDsl::class) fun `should compile dsl to recipe ingredient`() { val recipe = recipe("recipe") { diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerSpecTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt similarity index 91% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerSpecTest.kt rename to core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt index c6b549e3b..1c09c626e 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerSpecTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt @@ -55,6 +55,7 @@ import scala.Some import scala.Tuple2 import scala.collection.immutable.Seq import scala.concurrent.duration.Duration +import scala.concurrent.duration.FiniteDuration import java.util.Optional import java.util.concurrent.TimeUnit import kotlin.reflect.javaType @@ -65,7 +66,7 @@ class RecipeCompilerScalaTest { @Test fun `The recipe compiler should not have validation errors for a valid recipe`() { val recipe: Recipe = getRecipe("ValidRecipe") - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) } @@ -78,7 +79,7 @@ class RecipeCompilerScalaTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAfterFailure(Some(exhaustedEvent().name())) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -91,7 +92,7 @@ class RecipeCompilerScalaTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAfterFailure(Option.empty()) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -104,7 +105,7 @@ class RecipeCompilerScalaTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndBlock(Some(exhaustedEvent().name())) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -117,7 +118,7 @@ class RecipeCompilerScalaTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndBlock(Option.empty()) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -130,7 +131,7 @@ class RecipeCompilerScalaTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndResolve(Some(exhaustedEvent().name())) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -143,7 +144,7 @@ class RecipeCompilerScalaTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndResolve(Option.empty()) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("InteractionOneFunctionalFailed" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -161,7 +162,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -180,7 +181,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -199,7 +200,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -218,7 +219,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -237,7 +238,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -256,7 +257,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -275,7 +276,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -294,7 +295,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -313,7 +314,7 @@ class RecipeCompilerScalaTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("InteractionOneFunctionalFailed" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -344,7 +345,7 @@ class RecipeCompilerScalaTest { .withSensoryEvent(secondEvent()) .withInteractions(interactionOne()) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction" in compiledRecipe.validationErrors) } @@ -361,7 +362,7 @@ class RecipeCompilerScalaTest { .withSensoryEvent(initialEvent()) .withInteractions(wrongrecipeInstanceIdInteraction) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("Non supported process id type: Int32 on interaction: 'wrongrecipeInstanceIdInteraction'" in compiledRecipe.validationErrors) } @@ -378,7 +379,7 @@ class RecipeCompilerScalaTest { .withSensoryEvent(initialEvent()) .withInteractions(wrongMetaDataInteraction) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("Non supported MetaData type: Int32 on interaction: 'wrongMetaDataInteraction'" in compiledRecipe.validationErrors) } @@ -410,7 +411,7 @@ class RecipeCompilerScalaTest { .withSensoryEvents(initialEvent(), wrongDateEvent) .withInteractions(wrongMetaDataInteraction) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertEquals( listOf( "Ingredient 'RecipeInstanceMetaData' is provided and this is a reserved name for internal use in Baker", @@ -438,7 +439,7 @@ class RecipeCompilerScalaTest { ) .withSensoryEvent(initialEventInt) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("Interaction 'InteractionOne' expects ingredient 'initialIngredient:CharArray', however incompatible type: 'Record(data: Int32)' was provided" in compiledRecipe.validationErrors) } @@ -463,7 +464,7 @@ class RecipeCompilerScalaTest { ) .withSensoryEvents(initialEventIntOptional, initialEventIntOption) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionalInt:Option[Int32]', however incompatible type: 'Option[CharArray]' was provided" in compiledRecipe.validationErrors) assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionInt:Option[List[Int32]]', however incompatible type: 'Option[List[CharArray]]' was provided" in compiledRecipe.validationErrors) } @@ -485,7 +486,7 @@ class RecipeCompilerScalaTest { ) .withSensoryEvent(initialEventInt) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) } @@ -499,7 +500,7 @@ class RecipeCompilerScalaTest { ) .withSensoryEvent(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("Predefined argument 'initialIngredient' is not of type: CharArray on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) } @@ -512,7 +513,7 @@ class RecipeCompilerScalaTest { ) .withSensoryEvent(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue("Predefined argument 'WrongIngredient' is not defined on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) } @@ -538,7 +539,7 @@ class RecipeCompilerScalaTest { recipe("InteractionNameTest").withInteractions(invalidInteraction).withSensoryEvent(initialEvent()) assertFailsWith(exceptionMessage = "Interaction with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) + RecipeCompiler.compileRecipe(recipe) } } } @@ -550,7 +551,7 @@ class RecipeCompilerScalaTest { val recipe = recipe("EventNameTest").withSensoryEvent(invalidEvent).withInteraction(interactionOne()) assertFailsWith(exceptionMessage = "Event with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) + RecipeCompiler.compileRecipe(recipe) } } } @@ -563,7 +564,7 @@ class RecipeCompilerScalaTest { .withInteraction(interactionOne()) assertFailsWith(exceptionMessage = "Ingredient with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) + RecipeCompiler.compileRecipe(recipe) } } } @@ -574,7 +575,7 @@ class RecipeCompilerScalaTest { val recipe = recipe(name) assertFailsWith(exceptionMessage = "Recipe with a null or empty name found") { - RecipeCompilerKotlin.compileRecipe(recipe) + RecipeCompiler.compileRecipe(recipe) } } } @@ -585,7 +586,7 @@ class RecipeCompilerScalaTest { .withInteraction(interactionOne().isReprovider(true)) .withSensoryEvents(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertEquals( listOf("Reprovider interaction InteractionOne needs to have a event precondition"), compiledRecipe.validationErrors @@ -598,7 +599,7 @@ class RecipeCompilerScalaTest { .withInteraction(interactionWithOptionalIngredients()) .withSensoryEvent(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -620,7 +621,7 @@ class RecipeCompilerScalaTest { .withInteraction(interactionWithOptionalIngredients()) .withSensoryEvents(initialEvent(), optionalProviderEvent) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -657,7 +658,7 @@ class RecipeCompilerScalaTest { ) .withInteraction(secondInteraction) - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) val transition = @@ -679,7 +680,7 @@ class RecipeCompilerScalaTest { ) .withSensoryEvents(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -701,7 +702,7 @@ class RecipeCompilerScalaTest { .withInteraction(interactionWithOptionalIngredients()) .withSensoryEvents(initialEvent(), optionalProviderEvent) - val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -717,7 +718,7 @@ class RecipeCompilerScalaTest { @Test fun `The recipe compiler should give the correct id when it compiles a java recipe`() { val recipe = TestRecipeJava.getRecipe("id-test-recipe") - val compiledRecipeKotlin = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipeKotlin = RecipeCompiler.compileRecipe(recipe) val compiledRecipeScala = RecipeCompiler.compileRecipe(recipe) assertEquals(compiledRecipeScala.recipeId(), compiledRecipeKotlin.recipeId()) @@ -727,7 +728,7 @@ class RecipeCompilerScalaTest { @Test fun `The recipe compiler should give the interaction with Reprovider enabled when it compiles a java recipe and changes recipeId`() { val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) assertEquals("416e8abc02abcbee", compiledRecipe.recipeId()) } @@ -741,7 +742,7 @@ class RecipeCompilerScalaTest { emptySet>().asScala ).withRequiredEvent(initialEvent()) ) - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) assertEquals("469441173f91869a", compiledRecipe.recipeId()) assertEquals(1, compiledRecipe.petriNet().transitions().count { it is InteractionTransition && it.interactionName() @@ -793,7 +794,7 @@ class RecipeCompilerScalaTest { ) .withSubRecipe(subRecipe) - val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) val res = compiledRecipe.petriNet().transitions().asJava .flatMap { @@ -855,12 +856,12 @@ class RecipeCompilerScalaTest { fun Recipe.withSensoryEvents(vararg sensoryEvent: com.ing.baker.recipe.common.Event) = this.withSensoryEvents(sensoryEvent.asScala as Seq) - val Int.milliseconds get() = Duration.create(this.toLong(), TimeUnit.MILLISECONDS) - val Int.seconds get() = Duration.create(this.toLong(), TimeUnit.SECONDS) + val Int.milliseconds: FiniteDuration get() = Duration.create(this.toLong(), TimeUnit.MILLISECONDS) + val Int.seconds: FiniteDuration get() = Duration.create(this.toLong(), TimeUnit.SECONDS) object common { - val recipeInstanceIdName = `package$`.`MODULE$`.recipeInstanceIdName() - val recipeInstanceMetadataName = `package$`.`MODULE$`.recipeInstanceMetadataName() + val recipeInstanceIdName: String = `package$`.`MODULE$`.recipeInstanceIdName() + val recipeInstanceMetadataName: String = `package$`.`MODULE$`.recipeInstanceMetadataName() } @OptIn(ExperimentalStdlibApi::class) diff --git a/core/baker-interface-kotlin/pom.xml b/core/baker-interface-kotlin/pom.xml index 9f87742da..a943b7618 100644 --- a/core/baker-interface-kotlin/pom.xml +++ b/core/baker-interface-kotlin/pom.xml @@ -27,15 +27,6 @@ ${project.version} - - org.scala-lang - scala-library - - - org.scala-lang - scala-reflect - - org.jetbrains.kotlin kotlin-stdlib @@ -53,10 +44,6 @@ com.thoughtworks.paranamer paranamer - - org.scala-lang.modules - scala-collection-compat_${scala.compat.version} - org.jetbrains.kotlinx kotlinx-coroutines-core @@ -68,18 +55,14 @@ - org.scalatest - scalatest_${scala.compat.version} + org.junit.jupiter + junit-jupiter-engine test - org.scalacheck - scalacheck_${scala.compat.version} - test - - - org.scalatestplus - scalacheck-1-17_${scala.compat.version} + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin.version} test @@ -118,11 +101,6 @@ - - - org.scalatest - scalatest-maven-plugin - diff --git a/core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt b/core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt index 32b5ad3b4..db44d194f 100644 --- a/core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt +++ b/core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt @@ -1,8 +1,9 @@ -import org.junit.Assert.fail -import org.junit.Test + +import org.junit.jupiter.api.Test import java.lang.reflect.Method import kotlin.math.pow import kotlin.reflect.full.declaredFunctions +import kotlin.test.fail class BakerInterfaceTest { diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala index c2e7985a4..d4086bda8 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala @@ -35,7 +35,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { val result: IO[RecipeInstanceState] = for { baker <- InMemoryBaker.build(BakerF.Config(idleTimeout = 100.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List.empty) - recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(TestRecipe.getRecipe("InMemory")), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(TestRecipe.getRecipe("InMemory")), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) result <- baker.getRecipeInstanceState(recipeInstanceId) } yield (result) @@ -65,7 +65,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) @@ -95,7 +95,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(200.milliseconds) @@ -127,7 +127,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) @@ -157,7 +157,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(80.milliseconds) @@ -192,7 +192,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala index d5434a6e7..2fe97d408 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala @@ -49,7 +49,7 @@ abstract class BakerModelSpec def setupBakerWithRecipe(recipe: Recipe, implementations: List[InteractionInstance[IO]])(implicit effect: Sync[IO]): IO[(BakerF[IO], String)] = { for { baker <- buildBaker(implementations) - recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe), validate = true)) + recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = true)) } yield (baker, recipeId) } diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala index 7b83aff25..0c2fa85e8 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala @@ -32,7 +32,7 @@ trait BakerModelSpecEdgeCasesTests { self: BakerModelSpec => bakerAndRecipeId <- context.setupBakerWithRecipe(recipe, mockImplementations(sync, classTag))(sync) (baker, _) = bakerAndRecipeId - compiledRecipe = RecipeCompiler.compileRecipe(recipe) + compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) recipeId <- baker.addRecipe(RecipeRecord.of(compiledRecipe)) recipeInstanceId = UUID.randomUUID().toString diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala index 7edea189d..dc823b9bc 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala @@ -23,7 +23,7 @@ trait BakerModelSpecEnquireTests { self: BakerModelSpec => for { bakerWithRecipe <- context.setupBakerWithRecipe("returnAllRecipes", appendUUIDToTheRecipeName = false) (baker, recipeId) = bakerWithRecipe - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnAllRecipes2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnAllRecipes2")))) recipes <- baker.getAllRecipes _ = recipes.size shouldBe 2 _ = recipes(recipeId).compiledRecipe.name shouldBe "returnAllRecipes" @@ -44,7 +44,7 @@ trait BakerModelSpecEnquireTests { self: BakerModelSpec => for { bakerWithRecipe <- context.setupBakerWithRecipe("returnHealthAllRecipe", appendUUIDToTheRecipeName = false) (baker, recipeId) = bakerWithRecipe - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnHealthAllRecipe2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnHealthAllRecipe2")))) recipeInformations <- baker.getAllRecipes _ = recipeInformations.size shouldBe 2 _ = recipeInformations.get(recipeId) diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala index 44a217f82..c691f1b33 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala @@ -20,7 +20,7 @@ trait BakerModelSpecSetupTests { def runSetupTests()(implicit sync: Sync[IO], async: Async[IO], classTag: ClassTag[IO[Any]]): Unit = { test("correctly load extensions when specified in the configuration") { context => - val simpleRecipe = RecipeCompiler.compileRecipe(Recipe("SimpleRecipe") + val simpleRecipe = RecipeCompiler.INSTANCE.compileRecipe(Recipe("SimpleRecipe") .withInteraction(interactionOne) .withSensoryEvent(initialEvent)) @@ -52,7 +52,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.setupBakerWithNoRecipe(List(InteractionInstance.unsafeFrom(new InteractionOneSimple()))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } @@ -62,7 +62,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.setupBakerWithNoRecipe(List((InteractionInstance.unsafeFrom(new InteractionOneFieldName())))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } @@ -73,7 +73,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } @@ -83,7 +83,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(mock[ComplexIngredientInteraction]))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) } yield succeed } @@ -95,8 +95,8 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(mockImplementations) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe NonProvidedIngredient:68b775e508fc6877 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction")) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).attempt.map { + case Left(e) => e should have('message("Recipe NonProvidedIngredient:f31b181c8e36a5c3 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction")) case Right(_) => fail("Adding a recipe should fail") } } yield succeed @@ -110,8 +110,8 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List.empty) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe MissingImplementation:dc3970efc8837e64 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).attempt.map { + case Left(e) => e should have('message("Recipe MissingImplementation:e1b92d7afa5609d5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) case Right(_) => fail("Adding a recipe should fail") } } yield succeed @@ -125,8 +125,8 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(new InteractionOneWrongApply()))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe WrongImplementation:8e2745de0bb0bde5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).attempt.map { + case Left(e) => e should have('message("Recipe WrongImplementation:7fcdb6b8feb8aa87 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) case Right(_) => fail("Adding an interaction should fail") } } yield succeed @@ -142,7 +142,7 @@ trait BakerModelSpecSetupTests { .withRequiredEvents(interactionOneSuccessful)) - val compiled = RecipeCompiler.compileRecipe(recipe) + val compiled = RecipeCompiler.INSTANCE.compileRecipe(recipe) for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(testInteractionOneMock))) diff --git a/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala b/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala index c9be0f133..64dbb525c 100644 --- a/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala +++ b/core/baker-recipe-dsl/src/main/scala/com/ing/baker/recipe/scaladsl/CheckPointEvent.scala @@ -2,10 +2,6 @@ package com.ing.baker.recipe.scaladsl import com.ing.baker.recipe.common -object CheckPointEvent { - def apply(name: String): CheckPointEvent = CheckPointEvent(name, Set.empty, Set.empty) -} - case class CheckPointEvent private( override val name: String = "", override val requiredEvents: Set[String] = Set.empty, diff --git a/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala b/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala index 758404495..e5a48afaf 100644 --- a/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala +++ b/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala @@ -19,7 +19,7 @@ object WebshopBaker { val javaBaker: javadsl.Baker = new javadsl.Baker(baker) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(WebshopRecipe.recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebshopRecipe.recipe) val recipeId: String = Await.result(baker.addRecipe(compiledRecipe, validate = true), 10 seconds) } diff --git a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt deleted file mode 100644 index b7090a01b..000000000 --- a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/AssertionsKotlin.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.ing.baker.compiler - -import com.ing.baker.recipe.common.Event -import com.ing.baker.recipe.common.Ingredient -import com.ing.baker.recipe.common.InteractionDescriptor -import com.ing.baker.recipe.common.Recipe -import scala.Some - -private fun assertNoDuplicateElementsExist(compareIdentifier: (T) -> Any, elements: Iterable): Unit = - when (val duplicates = elements.groupBy { compareIdentifier(it) }.values.firstOrNull { it.size > 1 }) { - null -> Unit - else -> { - val e = duplicates[0] - val c = duplicates[1] - throw IllegalStateException("Duplicate identifiers found: ${e::class.java.getSimpleName()}:$e and ${c::class.java.getSimpleName()}:$c") - } - } - -private fun assertValidNames(nameFunc: (T) -> String?, list: Iterable, typeName: String): Unit = - if (list.map(nameFunc).any { name -> (name == null) || (name.isEmpty())}) - throw IllegalArgumentException("$typeName with a null or empty name found") - else - Unit - -private fun assertNonEmptyRecipe(recipe: Recipe): List = - listOf( - if (recipe.sensoryEvents.isEmpty()) "No sensory events found." else null, - if (recipe.interactions.isEmpty()) "No interactions found." else null, - ).filterNotNull() - -private fun assertRequiredEventForReprovider(recipe: Recipe): List = - recipe.interactions.filter { interaction -> - interaction.isReprovider && interaction.requiredEvents.isEmpty() && interaction.requiredOneOfEvents.isEmpty() - }.map{interaction -> "Reprovider interaction ${interaction.name()} needs to have a event precondition"} - - -private fun assertSensoryEventsNegativeFiringLimits(recipe: Recipe): List = - listOf( - if (recipe.sensoryEvents.map(Event::maxFiringLimit).filterIsInstance(Some::class.java).any { it.value() as Int <= 0 }) "MaxFiringLimit should be greater than 0" else null - ).filterNotNull() - -fun preCompileAssertions(recipe: Recipe): List { - assertValidNames(Recipe::name, listOf(recipe), "Recipe") - assertValidNames(InteractionDescriptor::name, recipe.interactions, "Interaction") - assertValidNames(Event::name, recipe.sensoryEvents, "Event") - val allIngredients = recipe.sensoryEvents.flatMap(Event::providedIngredients) + recipe.interactions.flatMap(InteractionDescriptor::inputIngredients) - assertValidNames(Ingredient::name, allIngredients, "Ingredient") - assertNoDuplicateElementsExist(InteractionDescriptor::name, recipe.interactions) - assertNoDuplicateElementsExist(Event::name, recipe.sensoryEvents) - return assertSensoryEventsNegativeFiringLimits(recipe) + assertRequiredEventForReprovider(recipe) -} \ No newline at end of file diff --git a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt deleted file mode 100644 index 7fd879967..000000000 --- a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt +++ /dev/null @@ -1,680 +0,0 @@ -package com.ing.baker.compiler - -import com.ing.baker.compiler.ScalaConversions.asJava -import com.ing.baker.compiler.ScalaConversions.asScala -import com.ing.baker.il.* -import com.ing.baker.il.CompiledRecipe.OldRecipeIdVariant -import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff -import com.ing.baker.il.petrinet.Edge -import com.ing.baker.il.petrinet.MissingEventTransition -import com.ing.baker.il.petrinet.Place -import com.ing.baker.il.petrinet.Transition -import scala.Option -import scala.util.Left -import scala.util.Right - -import com.ing.baker.il.petrinet.* -import com.ing.baker.il.petrinet.Place.FiringLimiterPlace -import com.ing.baker.petrinet.api.* -import com.ing.baker.recipe.common.* -import com.ing.baker.recipe.common.EventOutputTransformer -import com.ing.baker.recipe.scaladsl.`Event$` -import com.ing.baker.types.* -import scala.Some -import scala.collection.immutable.Seq -import scala.reflect.ClassTag -import scala.util.Either -import scalax.collection.GraphEdge -import scalax.collection.config.CoreConfig -import scalax.collection.config.GraphConfig -import scalax.collection.edge.WLDiEdge -import scalax.collection.edge.`WLDiEdge$` -import scalax.collection.`Graph$` -import scalax.collection.Graph -import scalax.collection.mutable.ArraySet.Hints - -/** - * Type alias for a petri net with recipe Place and Transition types - */ -typealias RecipePetriNet = PetriNet - -/** - * Type alias for the node type of the scalax.collection.Graph backing the petri net. - */ -typealias Node = Either - -/** - * Type alias for the edge type of the scalax.collection.Graph backing the petri net. - */ -typealias Arc = WLDiEdge - -/** - * Type alias for a multi set. - */ -typealias MultiSet = Map - -/** - * Type alias for a marking. - */ -typealias Marking = Map> - - - -object RecipeCompilerKotlin { - fun List, List>>.unzipFlatten() = this.unzip().let { pair -> - pair.first.flatten() to pair.second.flatten() } - - - fun transition(transition: Transition) = Right(transition) - fun place(place: Place) = Left(place) - - fun arc(t: Transition, p: Place, weight: Long): Arc = - wlDiEdge(transition(t), place(p), weight.toDouble(), Edge(Option.empty())) - - - fun arc(p: Place, t: Transition, weight: Long, eventFilter: String? = null): Arc = - wlDiEdge(place(p), transition(t), weight.toDouble(), Edge(Option.apply(eventFilter))) - - /** - * Creates a transition for a missing event in the recipe. - */ - private fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) - - private fun buildEventAndPreconditionArcs( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition) = - - //Find the event in available events - interaction.requiredEvents.map { eventName -> - // a new `Place` generated for each AND events - val eventPreconditionPlace = - createPlace( - label = "$eventName-${interaction.name()}", - placeType = Place.`EventPreconditionPlace$`.`MODULE$` - ) - - buildEventPreconditionArcs( - eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name()) - ) - }.unzipFlatten() - - - private fun buildEventORPreconditionArcs( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition) = - - interaction.requiredOneOfEvents.mapIndexed { index: Int, orGroup: Set -> - // only one `Place` for all the OR events - val eventPreconditionPlace = createPlace( - label = "${interaction.name()}-or-$index", - placeType = Place.`EventPreconditionPlace$`.`MODULE$` - ) - - orGroup.map { eventName -> - buildEventPreconditionArcs(eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name())) - }.unzipFlatten() - }.unzipFlatten() - - private fun buildEventPreconditionArcs(eventName: String, - preconditionPlace: Place, - preconditionTransition: (String) -> Transition?, - interactionTransition: Transition - ): Pair, List> { - - val eventTransition: Transition? = preconditionTransition(eventName) - - val notProvidedError: List = when(eventTransition) { - null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") - else -> emptyList() - } - - val arcs: List = listOf( - arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace, 1), - arc(preconditionPlace, interactionTransition, 1) - ) - - return arcs to notProvidedError - } - - // the (possible) event output arcs / places - private fun buildInteractionOutputArcs( - interaction: InteractionTransition, - eventTransitions: List) = - if (interaction.eventsToFire.isNotEmpty()) { - val resultPlace = createPlace(label = interaction.label(), placeType = Place.`InteractionEventOutputPlace$`.`MODULE$`) - val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor -> - //Get the correct event transition - val eventTransition = eventTransitions.find {it.event().name() == event.name() } ?: throw IllegalStateException("eventTransition should be found") - //Decide if there are multiple interactions that fire this transition, - // if so create a event combiner place - // else link the transition to the event. - val eventTransitionCount = eventTransitions.count { e -> e.event().name() == event.name() } - if(eventTransitionCount > 1) { - //Create a new intermediate event place - val eventCombinerPlace: Place = createPlace(label = event.name(), placeType = Place.`IntermediatePlace$`.`MODULE$`) - //Create a new intermediate event transition - val interactionToEventTransition: IntermediateTransition = IntermediateTransition("${interaction.interactionName()}:${event.name()}") - //link the interaction output place to the intermediate transition - val interactionOutputPlaceToIntermediateTransition: Arc = arc(resultPlace, interactionToEventTransition, 1, event.name()) - //link the intermediate transition to the intermediate input place - val intermediateTransitionToEventCombinerPlace: Arc = arc(interactionToEventTransition, eventCombinerPlace, 1) - //Link the intermediate place to the event place - val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) - listOf(intermediateTransitionToEventCombinerPlace, interactionOutputPlaceToIntermediateTransition, eventCombinerPlaceToEventTransition) - } - else { - val internalEventTransition = eventTransition - listOf(arc(resultPlace, internalEventTransition, 1, event.name())) - } - } - eventArcs + arc(interaction, resultPlace, 1) - } - else emptyList() - - /** - * Draws an arc from all required ingredients for an interaction - * If the ingredient has multiple consumers create a multi transition place and create both arcs for it - */ - private fun buildInteractionInputArcs( - t: InteractionTransition, - multipleConsumerFacilitatorTransitions: List, - ingredientsWithMultipleConsumers: Map>): List { - - val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = - t.nonProvidedIngredients.map(IngredientDescriptor::name).partition(ingredientsWithMultipleConsumers::contains) - - // the extra arcs to model multiple output transitions from one place - val internalDataInputArcs = fieldNamesWithPrefixMulti.flatMap { fieldName -> - val multiTransitionPlace = createPlace("${t.label()}-$fieldName", placeType = Place.`MultiTransitionPlace$`.`MODULE$`) - listOf( - // one arc from multiplier place to the transition - arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), - multiTransitionPlace, - 1), - // one arc from extra added place to transition - arc(multiTransitionPlace, t, 1) - ) - } - - - // the data input arcs / places - val dataInputArcs: List = fieldNamesWithoutPrefix.map { fieldName -> arc(createPlace(fieldName, Place.`IngredientPlace$`.`MODULE$`), t, 1) } - - val dataOutputArcs: List = - if (t.isReprovider) - fieldNamesWithoutPrefix.map { fieldName -> arc(t, createPlace(fieldName, Place.`IngredientPlace$`.`MODULE$`), 1) } + - fieldNamesWithPrefixMulti.map { fieldName -> arc(t, createPlace("${t.label()}-$fieldName", placeType = Place.`MultiTransitionPlace$`.`MODULE$`), 1) } - else - emptyList() - - val limitInteractionCountArc: List = when (val maximumInteractionCount = t.maximumInteractionCount) { - null -> emptyList() - else -> listOf(arc(createPlace("limit:${t.label()}", Place.FiringLimiterPlace(maximumInteractionCount)), t, 1)) - } - - return dataInputArcs + dataOutputArcs + internalDataInputArcs + limitInteractionCountArc - } - - private fun buildInteractionArcs( - multipleOutputFacilitatorTransitions: List, - placeNameWithDuplicateTransitions: Map>, - eventTransitions: List, interactionTransition: InteractionTransition - )= - buildInteractionInputArcs( - interactionTransition, - multipleOutputFacilitatorTransitions, - placeNameWithDuplicateTransitions - ) + buildInteractionOutputArcs( - interactionTransition, - eventTransitions - ) - - /** - * Compile the given recipe to a technical recipe that is useful for Baker. - * - * @param recipe ; The Recipe to compile and execute - * @param validationSettings The validation settings to follow for the validation - * @return - */ - fun compileRecipe( - recipe: Recipe, - validationSettings: ValidationSettings - ): CompiledRecipe { - - - val precompileErrors: List = preCompileAssertions(recipe) - - // Extend the interactions with the checkpoint event interactions and sub-recipes - val actionDescriptors: List = recipe.interactions + - recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + - recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + - recipe.sieves.map(::convertSieveToInteraction)//checkPointSieveSubRecipeInteractions(recipe) - - fun subRecipeSensoryEvents(recipe: Recipe): Set = - recipe.subRecipes.flatMap(com.ing.baker.recipe.common.Recipe::sensoryEvents).toSet() - - //All ingredient names provided by sensory events or by interactions - val allIngredientNames: Set = - (recipe.sensoryEvents + - subRecipeSensoryEvents(recipe)).flatMap { e -> e.providedIngredients.map(Ingredient::name) } - .toSet() + - actionDescriptors.flatMap { i -> - i.output.flatMap { e -> - // check if the event was renamed (check if there is a transformer for this event) - when (val transformer = i.eventOutputTransformers[e]) { - null -> e.providedIngredients.map(Ingredient::name) - else -> e.providedIngredients.map { ingredient -> - transformer.ingredientRenames[ingredient.name()] ?: ingredient.name() - } - } - } - } - - // - // For inputs for which no matching output cannot be found, we do not want to generate a place. - // It should be provided at runtime from outside the active petri net (marking) - val interactionTransitions = - actionDescriptors.map { interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) } - - val allInteractionTransitions: List = interactionTransitions - - // events provided from outside - //TODO: handle sensoryEvents subrecipe Recursion - val sensoryEventTransitions: List = (recipe.sensoryEvents + recipe.subRecipes.flatMap {it.sensoryEvents }).map { - event -> EventTransition(eventToCompiledEvent(event), true, event.maxFiringLimit()) - } - - // events provided by other transitions / actions - val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> - t.eventsToFire.map { event -> EventTransition (event, false, Option.empty()) } - } - - val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions - - // Given the event classes, it is creating the ingredient places and - // connecting a transition to a ingredient place. - val internalEventArcs: List = allInteractionTransitions.flatMap { t -> - t.eventsToFire.flatMap { event -> - event.ingredients.map { - ingredient -> - val from = interactionEventTransitions.find { it.label() == event.name() } ?: throw IllegalStateException() - arc(from, createPlace(ingredient.name(), Place.`IngredientPlace$`.`MODULE$`), 1) - } - } - } - - //Create event limiter places so that events can only fire x amount of times. - val eventLimiterArcs: List = sensoryEventTransitions.flatMap { t -> when(val n = t.maxFiringLimit().getOrElse {null as Int?}) { - null -> emptyList() - else -> listOf(arc(createPlace("limit:${t.label()}", Place.FiringLimiterPlace(n)), t, 1)) - }} - - - fun findEventTransitionByEventName(eventName: String) = allEventTransitions.find { it.event().name() == eventName } - - fun findInteractionByLabel (label: String) = allInteractionTransitions.find { it.label() == label } ?: throw IllegalStateException() - - // This generates precondition arcs for Required Events (AND). - val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { - t -> - buildEventAndPreconditionArcs( - t, - ::findEventTransitionByEventName, - ::findInteractionByLabel - ) - }.unzipFlatten() - - // This generates precondition arcs for Required Events (OR). - val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { - t -> - buildEventORPreconditionArcs( - t, - ::findEventTransitionByEventName, - ::findInteractionByLabel - ) - }.unzipFlatten() - - val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } - - // It connects a sensory event to an ingredient places - val sensoryEventArcs: List = sensoryEventWithIngredients.flatMap { et -> - et.event().ingredients.map { ingredient -> - arc(et, createPlace(ingredient.name(), Place.`IngredientPlace$`.`MODULE$`), 1) - } - } - - val eventThatArePreconditions: List = actionDescriptors.flatMap { - actionDescriptor -> actionDescriptor.requiredEvents + actionDescriptor.requiredOneOfEvents.flatten() - } - - // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net - val sensoryEventArcsNoIngredientsArcs: List = sensoryEventWithoutIngredients - //Filter out events that are preconditions to interactions - .filterNot { sensoryEvent -> eventThatArePreconditions.contains(sensoryEvent.label()) } - .map { sensoryEvent -> arc(sensoryEvent, createPlace(sensoryEvent.label(), Place.`EmptyEventIngredientPlace$`.`MODULE$`), 1) } - - // First find the cases where multiple transitions depend on the same ingredient place - val ingredientsWithMultipleConsumers: Map> = - getIngredientsWithMultipleConsumers(allInteractionTransitions) - - // Add one new transition for each duplicate input (the newly added one in the image above) - val multipleConsumerFacilitatorTransitions: List = - ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) - - val multipleOutputFacilitatorArcs: List = - multipleConsumerFacilitatorTransitions.map { t -> - arc(createPlace(t.label(), Place.`IngredientPlace$`.`MODULE$`), t, 1) } - - val interactionArcs: List = - allInteractionTransitions.flatMap { interactionTransition -> - buildInteractionArcs( - multipleConsumerFacilitatorTransitions, - ingredientsWithMultipleConsumers, - interactionEventTransitions, - interactionTransition - ) - } - - val arcs = (interactionArcs + - eventPreconditionArcs + - eventOrPreconditionArcs + - eventLimiterArcs + - sensoryEventArcs + - sensoryEventArcsNoIngredientsArcs + - internalEventArcs + - multipleOutputFacilitatorArcs) - - val petriNet: PetriNet = PetriNet(graph(arcs)) - - val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> - when (val placeType = p.placeType()) { - is FiringLimiterPlace -> - p to mapOf(null to placeType.maxLimit()) - - else -> null - } - }.toMap() - - val errors = preconditionORErrors + preconditionANDErrors + precompileErrors - - val oldRecipeIdVariant: OldRecipeIdVariant = - when (recipe) { - is com.ing.baker.recipe.javadsl.Recipe -> CompiledRecipe.`Scala212CompatibleJava$`.`MODULE$` - is com.ing.baker.recipe.kotlindsl.Recipe -> CompiledRecipe.`Scala212CompatibleKotlin$`.`MODULE$` - is com.ing.baker.recipe.scaladsl.Recipe -> CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` - else -> throw IllegalArgumentException("Only recipe of type java, kotlin or scala supported") - } - - val compiledRecipe = CompiledRecipe.build( - recipe.name(), - petriNet, - initialMarking.mapValues { it.value.mapValues{ it.value as Any}.asScala }.asScala, - errors.asScala, - recipe.eventReceivePeriod(), - recipe.retentionPeriod(), - oldRecipeIdVariant, - ) - - return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) - } - - fun compileRecipe(recipe: Recipe): CompiledRecipe = compileRecipe(recipe, `ValidationSettings$`.`MODULE$`.defaultValidationSettings()) - - - - private fun getMultiTransition(internalRepresentationName: String, transitions: List) = - transitions.find { it.label().equals(internalRepresentationName) } ?: - throw NoSuchElementException("No multi transition found with name $internalRepresentationName") - - /** - * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. - * - * @param actionTransitions Seq of reflected transition. - * @return A map from input place name to reflected transitions (where the transitions have as input the place). - */ - private fun getIngredientsWithMultipleConsumers(actionTransitions: List): Map> = - // Obtain a list of field name with their transition - actionTransitions.flatMap { transition -> - transition.nonProvidedIngredients.map { ingredient -> - ingredient.name() to transition - } - }.groupBy({ it.first}, { it.second}) - - - private fun createPlace(label: String, placeType: Place.PlaceType): Place = - Place("${placeType.labelPrepend()}$label", placeType) - - private fun convertCheckpointEventToInteraction(e: com.ing.baker.recipe.common.CheckPointEvent) = - interaction( - name = "${com.ing.baker.il.`package$`.`MODULE$`.checkpointEventInteractionPrefix()}${e.name()}", - inputIngredients = emptyList(), - output = listOf(`Event$`.`MODULE$`.apply(e.name(), emptyList().asScala)), - requiredEvents = e.requiredEvents, - requiredOneOfEvents = e.requiredOneOfEvents - ) - - private fun convertSieveToInteraction(s: com.ing.baker.recipe.common.Sieve) = - interaction( - name = "${com.ing.baker.il.`package$`.`MODULE$`.sieveInteractionPrefix()}${s.name()}", - inputIngredients = s.inputIngredients, - output = s.output, - requiredEvents = emptySet(), - requiredOneOfEvents = emptySet() - ) - - - private fun flattenSubRecipesToInteraction(recipe: com.ing.baker.recipe.common.Recipe): Set { - fun copyInteraction(i: InteractionDescriptor) = com.ing.baker.recipe.scaladsl.Interaction.apply( - "${com.ing.baker.il.`package$`.`MODULE$`.subRecipePrefix()}${recipe.name()}$$${i.name()}", - i.inputIngredients(), - i.output(), - i.requiredEvents(), - i.requiredOneOfEvents(), - i.predefinedIngredients(), - i.overriddenIngredientNames(), - i.overriddenOutputIngredientName(), - i.maximumInteractionCount(), - i.failureStrategy(), - i.eventOutputTransformers(), - i.isReprovider, - Option.apply(i.originalName()) - ) - return recipe.interactions.map(::copyInteraction).toSet() + checkPointSieveSubRecipeInteractions(recipe) - } - - private fun checkPointSieveSubRecipeInteractions(recipe: com.ing.baker.recipe.common.Recipe): List = - recipe.checkpointEvents.map(::convertCheckpointEventToInteraction).toList() + - recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + - recipe.sieves.map(::convertSieveToInteraction).toList() - - - -} - -fun ingredientToCompiledIngredient(ingredient: Ingredient): IngredientDescriptor = IngredientDescriptor(ingredient.name(), ingredient.ingredientType()) - -fun eventToCompiledEvent(event: Event): EventDescriptor = EventDescriptor(event.name(), event.providedIngredients.map(::ingredientToCompiledIngredient).asScala) - - -private fun graph(arcs: List): Graph> { - val defaultHints = object : Hints { - override fun initialCapacity() = 16 - override fun capacityIncrement() = 32 - override fun hashTableThreshold() = 48 - override fun compactUpToUsed() = 80 - } - - return `Graph$`.`MODULE$`.from(arcs.asScala, ClassTag.apply(WLDiEdge::class.java), CoreConfig( - GraphConfig.defaultOrder(), defaultHints - )) as Graph> -} - - -private fun wlDiEdge( - node1: Node, - node2: Node, - weight: Double, - label: L -): WLDiEdge = - `WLDiEdge$`.`MODULE$`.newEdge( - GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), - weight, - label - ) - -fun interactionTransitionOf( - interactionDescriptor: InteractionDescriptor, - defaultFailureStrategy: InteractionFailureStrategy, - allIngredientNames: Set -): InteractionTransition { - //This transforms the event using the eventOutputTransformer to the new event - //If there is no eventOutputTransformer for the event the original event is returned - fun transformEventType(event: Event): Event = - when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers.get(event)) { - null -> event - else -> com.ing.baker.recipe.scaladsl.Event( - eventOutputTransformer.newEventName, - event.providedIngredients.map { i -> - Ingredient(eventOutputTransformer.ingredientRenames.getOrElse(i.name(), { i.name() }), i.ingredientType())}.asScala, - Option.empty() - ) - } - - fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = - com.ing.baker.il.EventOutputTransformer(recipeEventOutputTransformer.newEventName(), recipeEventOutputTransformer.ingredientRenames()) - - fun transformEventToCompiledEvent(event: Event): EventDescriptor = - EventDescriptor( - event.name(), - event.providedIngredients.map(::ingredientToCompiledIngredient).asScala) - - // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe - // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe - // Replace BakerEventList to BakerEventList tag as know in compiledRecipe - // Replace ingredient tags with overridden tags - val inputFields: Seq> = interactionDescriptor.inputIngredients - .map { ingredient -> - when (ingredient.name()) { - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> com.ing.baker.il.`package$`.`MODULE$`.recipeInstanceIdName() - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> com.ing.baker.il.`package$`.`MODULE$`.recipeInstanceMetadataName() - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> com.ing.baker.il.`package$`.`MODULE$`.recipeInstanceEventListName() - else -> interactionDescriptor.overriddenIngredientNames().getOrElse(ingredient.name(), {ingredient.name()}) - } to ingredient.ingredientType() - }.asScala - - val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala - val eventsToFire = interactionDescriptor.output.map(::transformEventType).map(::transformEventToCompiledEvent).asScala - - //For each ingredient that is not provided - //And is of the type Optional or Option - //Add it to the predefinedIngredients List as empty - //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. - val predefinedIngredientsWithOptionalsEmpty: Map = - inputFields.asJava - .filter { (name, type) -> type is OptionType && name !in allIngredientNames } - .map { Pair(it.first, `NullValue$`.`MODULE$`) }.toMap() + - interactionDescriptor.predefinedIngredients - - val p: Pair> = - when (val strategy = interactionDescriptor.failureStrategy().getOrElse {defaultFailureStrategy }) { - is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { - val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { - is Some -> Some(EventDescriptor( - e.value().getOrElse { null as String? } ?: (interactionDescriptor.name() + com.ing.baker.il.`package$`.`MODULE$`.exhaustedEventAppend()), emptyList().asScala) - ) - else -> Option.empty() - } - RetryWithIncrementalBackoff(strategy.initialDelay(), strategy.backoffFactor(), strategy.maximumRetries(), strategy.maxTimeBetweenRetries(), exhaustedRetryEvent) to exhaustedRetryEvent - } - is InteractionFailureStrategy.BlockInteraction -> com.ing.baker.il.failurestrategy.`BlockInteraction$`.`MODULE$` to Option.empty() - is InteractionFailureStrategy.FireEventAfterFailure -> { - val eventName = strategy.eventName().getOrElse { interactionDescriptor.name() + com.ing.baker.il.`package$`.`MODULE$`.exhaustedEventAppend() } - val exhaustedRetryEvent: EventDescriptor = EventDescriptor(eventName, emptyList().asScala) - com.ing.baker.il.failurestrategy.FireEventAfterFailure(exhaustedRetryEvent) to Some(exhaustedRetryEvent) - } - else -> com.ing.baker.il.failurestrategy.`BlockInteraction$`.`MODULE$` to Option.empty() - } - val failureStrategy = p.first - val exhaustedRetryEvent = p.second.toList().toSeq() - - return InteractionTransition( - (eventsToFire.asJava + exhaustedRetryEvent.asJava).asScala, - (originalEvents.asJava + exhaustedRetryEvent.asJava).asScala, - inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala, - interactionDescriptor.name(), - interactionDescriptor.originalName(), - predefinedIngredientsWithOptionalsEmpty.asScala, - interactionDescriptor.maximumInteractionCount(), - failureStrategy, - interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> - event.name() to transformEventOutputTransformer( - transformer - ) - }.toMap().asScala, - interactionDescriptor.isReprovider() - ) -} - - - -private fun interaction( - name: String?, - inputIngredients: List, - output: List, - requiredEvents: kotlin.collections.Set = emptySet(), - requiredOneOfEvents: kotlin.collections.Set> = emptySet(), - predefinedIngredients: Map = emptyMap(), - overriddenIngredientNames: Map = emptyMap(), - overriddenOutputIngredientName: String? = null, - maximumInteractionCount: Int? = null, - failureStrategy: InteractionFailureStrategy? = null, - eventOutputTransformers: Map = emptyMap(), - isReprovider: Boolean = false, - oldName: String? = null, -) = - com.ing.baker.recipe.scaladsl.Interaction.apply( - name, - inputIngredients.asScala, - output.asScala, - requiredEvents.asScala, - requiredOneOfEvents.map { it: kotlin.collections.Set -> it.asScala}.toSet().asScala, - predefinedIngredients.asScala, - overriddenIngredientNames.asScala, - Option.apply(overriddenOutputIngredientName), - Option.apply(maximumInteractionCount), - Option.apply(failureStrategy), - eventOutputTransformers.asScala, - isReprovider, - Option.apply(oldName) - ) - -val Recipe.sensoryEvents get() = this.sensoryEvents().asJava -val Recipe.interactions get() = this.interactions().asJava -val Recipe.subRecipes get() = this.subRecipes().asJava -val Recipe.checkpointEvents get() = this.checkpointEvents().asJava -val Recipe.sieves get() = this.sieves().asJava -val InteractionDescriptor.requiredEvents get() = this.requiredEvents().asJava -val InteractionDescriptor.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() -val InteractionDescriptor.output get() = this.output().asJava -val InteractionDescriptor.eventOutputTransformers get() = this.eventOutputTransformers().asJava -val InteractionDescriptor.inputIngredients get() = this.inputIngredients().asJava -val InteractionDescriptor.predefinedIngredients get() = this.predefinedIngredients().asJava -val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava -val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava -val InteractionTransition.maximumInteractionCount get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } -val Event.providedIngredients get() = this.providedIngredients().asJava -val EventDescriptor.ingredients get() = this.ingredients().asJava -val EventOutputTransformer.ingredientRenames get() = this.ingredientRenames().asJava -val EventOutputTransformer.newEventName get() = this.newEventName() -val CheckPointEvent.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() -val CheckPointEvent.requiredEvents get() = this.requiredEvents().asJava -val Sieve.inputIngredients get() = this.inputIngredients().asJava -val Sieve.output get() = this.output().asJava diff --git a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt b/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt deleted file mode 100644 index 3970dffee..000000000 --- a/core/recipe-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.ing.baker.compiler - -import scala.`$less$colon$less` -import scala.Predef -import scala.Tuple2 -import scala.collection.immutable.Seq -import scala.jdk.CollectionConverters.* - -object ScalaConversions { - val Array.asScala get(): scala.collection.immutable.Seq = ListHasAsScala(this.toList()).asScala().toSeq() - val List.asScala get(): scala.collection.immutable.Seq = ListHasAsScala(this).asScala().toSeq() - val Set.asScala get(): scala.collection.immutable.Set = SetHasAsScala(this).asScala().toSet() - val Map.asScala get(): scala.collection.immutable.Map = MapHasAsScala(this).asScala().toMap( - Predef.`$conforms`>() as `$less$colon$less`, Tuple2>?) - - val Seq.asJava get(): List = SeqHasAsJava(this).asJava() - val scala.collection.immutable.Set.asJava get(): Set = SetHasAsJava(this).asJava() - val scala.collection.immutable.Map.asJava get(): Map = MapHasAsJava(this).asJava() -} - diff --git a/docs/archive/baker-runtime.md b/docs/archive/baker-runtime.md index aae941116..a405d543f 100644 --- a/docs/archive/baker-runtime.md +++ b/docs/archive/baker-runtime.md @@ -78,13 +78,13 @@ For this purpose there is the `RecipeCompiler`. === "Scala" ```scala - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) ``` === "Java" ```java - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); ``` !!! hint "Did you know?!" @@ -117,7 +117,7 @@ ValidateOrderImpl validateOrderImpl = new ValidateOrderImpl(); ManufactureGoodsImpl manufactureGoodsImpl = new ManufactureGoodsImpl(); // Compiling the Recipe -CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); +CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); // Creating a Baker Runtime JBaker baker = new JBaker(); diff --git a/docs/archive/recipe-visualization.md b/docs/archive/recipe-visualization.md index 22448c919..21284064a 100644 --- a/docs/archive/recipe-visualization.md +++ b/docs/archive/recipe-visualization.md @@ -9,7 +9,7 @@ Baker can turn a recipe into a .dot representation. `.dot` is a notation for rep === "Scala" ``` scala - val recipe = RecipeCompiler.compileRecipe(Examples.webshop.webShopRecipe) + val recipe = RecipeCompiler.INSTANCE.compileRecipe(Examples.webshop.webShopRecipe) println(recipe.getRecipeVisualization) ``` @@ -17,7 +17,7 @@ Baker can turn a recipe into a .dot representation. `.dot` is a notation for rep === "Java" ```java - final CompiledRecipe recipe = RecipeCompiler.compileRecipe(Examples.webshop.webShopRecipe); + final CompiledRecipe recipe = RecipeCompiler.INSTANCE.compileRecipe(Examples.webshop.webShopRecipe); System.out.println(recipe.getRecipeVisualization()); ``` diff --git a/docs/sections/reference/main-abstractions.md b/docs/sections/reference/main-abstractions.md index 30b0c981c..844de0f86 100644 --- a/docs/sections/reference/main-abstractions.md +++ b/docs/sections/reference/main-abstractions.md @@ -524,7 +524,7 @@ the `Interaction` ActorSystem("WebshopSystem") val baker: Baker = AkkaBaker.localDefault(actorSystem) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(WebshopRecipe.recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebshopRecipe.recipe) val program: Future[Unit] = for { _ <- baker.addInteractionInstance(WebshopInstances.ReserveItemsInstance) @@ -567,7 +567,7 @@ the `Interaction` EventInstance.from(new JWebshopRecipe.OrderPlaced("order-uuid", items)); InteractionInstance reserveItemsInstance = InteractionInstance.from(new ReserveItems()); - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); String recipeInstanceId = "first-instance-id"; CompletableFuture> result = baker.addInteractionInstance(reserveItemsInstance) diff --git a/docs/sections/reference/runtime.md b/docs/sections/reference/runtime.md index bb8290135..737308169 100644 --- a/docs/sections/reference/runtime.md +++ b/docs/sections/reference/runtime.md @@ -221,7 +221,7 @@ into a [visualization](../../cookbook/visualizations). import com.ing.baker.compiler.RecipeCompiler import com.ing.baker.il.CompiledRecipe - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) ``` === "Java" @@ -230,7 +230,7 @@ into a [visualization](../../cookbook/visualizations). import com.ing.baker.compiler.RecipeCompiler; import com.ing.baker.il.CompiledRecipe; - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); ``` ## baker.addRecipe(RecipeRecord.of(compiledRecipe)) diff --git a/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala b/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala index 21a5d00b3..b9bf110f7 100644 --- a/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala +++ b/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala @@ -17,7 +17,7 @@ object WebShopBaker { val logger: Logger = getLogger("webshop.webservice") val checkoutFlowCompiledRecipe: CompiledRecipe = - RecipeCompiler.compileRecipe(CheckoutFlowRecipe.recipe) + RecipeCompiler.INSTANCE.compileRecipe(CheckoutFlowRecipe.recipe) def initRecipes(baker: Baker)(implicit ec: ExecutionContext): IO[String] = { IO.fromFuture(IO(for { diff --git a/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java b/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java index da8c8242e..9bfefd392 100644 --- a/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java +++ b/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java @@ -30,12 +30,12 @@ public class JWebshopRecipeTests { @Test public void shouldCompileTheRecipeWithoutIssues() { - RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); + RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); } @Test public void shouldVisualizeTheRecipeWithoutIssues() { - CompiledRecipe recipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe recipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); String visualization = recipe.getRecipeVisualization(); System.out.println(Console.GREEN() + "Recipe visualization, paste this into webgraphviz.com:"); System.out.println(visualization + Console.RESET()); @@ -44,7 +44,7 @@ public void shouldVisualizeTheRecipeWithoutIssues() { @Test public void shouldRunSimpleInstance() throws ExecutionException, InterruptedException { // Compile the recipe - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); List implementations = ImmutableList.of( new MakePaymentInstance(), @@ -88,7 +88,7 @@ public void shouldRunSimpleInstance() throws ExecutionException, InterruptedExce @Test public void shouldRunSimpleInstanceMockitoSample() throws InterruptedException, TimeoutException, ExecutionException { // Compile the recipe - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); // Setup mock ReserveItems reserveItemsMock = diff --git a/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala b/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala index 0c763bf9b..84ae0ad22 100644 --- a/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala +++ b/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala @@ -25,17 +25,17 @@ class WebshopRecipeSpec extends TestKit(ActorSystem("baker-webshop-system")) wit } "The WebshopRecipeReflection" should "compile the recipe without errors" in { - RecipeCompiler.compileRecipe(SimpleWebshopRecipeReflection.recipe) + RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipeReflection.recipe) Future.successful(succeed) } "The WebshopRecipe" should "compile the recipe without errors" in { - RecipeCompiler.compileRecipe(SimpleWebshopRecipe.recipe) + RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipe.recipe) Future.successful(succeed) } it should "visualize the recipe" in { - val compiled = RecipeCompiler.compileRecipe(SimpleWebshopRecipe.recipe) + val compiled = RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipe.recipe) val viz: String = compiled.getRecipeVisualization println(Console.GREEN + s"Recipe visualization, paste this into webgraphviz.com:") println(viz + Console.RESET) @@ -71,7 +71,7 @@ class WebshopRecipeSpec extends TestKit(ActorSystem("baker-webshop-system")) wit InteractionInstance.unsafeFrom(new ReserveItemsMock) val baker: Baker = AkkaBaker.localDefault(system, CachingInteractionManager(reserveItemsInstance)) - val compiled = RecipeCompiler.compileRecipe(SimpleWebshopRecipe.recipe) + val compiled = RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipe.recipe) val recipeInstanceId: String = UUID.randomUUID().toString val orderId: String = "order-id" diff --git a/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala b/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala index e7cc64144..8467ba478 100644 --- a/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala +++ b/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala @@ -14,7 +14,7 @@ object Main extends IOApp { override def run(args: List[String]): IO[ExitCode] = { - val compiled = RecipeCompiler.compileRecipe(CheckoutFlowRecipe.recipe) + val compiled = RecipeCompiler.INSTANCE.compileRecipe(CheckoutFlowRecipe.recipe) val checkoutRecipeId = compiled.recipeId /* val protoRecipe: Array[Byte] = ProtoMap.ctxToProto(compiled).toByteArray diff --git a/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala b/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala index 78076e30f..7a2ad999b 100644 --- a/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala +++ b/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala @@ -12,7 +12,7 @@ import java.util.UUID object WebShopBaker { val checkoutFlowCompiledRecipe: CompiledRecipe = - RecipeCompiler.compileRecipe(CheckoutFlowRecipe.recipe) + RecipeCompiler.INSTANCE.compileRecipe(CheckoutFlowRecipe.recipe) } class WebShopBaker(baker: Baker, checkoutRecipeId: String) extends WebShop with LazyLogging { diff --git a/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java b/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java index 0c5b00013..e1a5a8ece 100644 --- a/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java +++ b/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java @@ -25,7 +25,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc var recipeInstanceId = UUID.randomUUID().toString(); var sensoryEvent = EventInstance.from(createOrderPlaced()); - var recipeId = baker.addRecipe(RecipeCompiler.compileRecipe(WebShopRecipe.recipe), true).get(); + var recipeId = baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe), true).get(); baker.bake(recipeId, recipeInstanceId).get(); baker.fireSensoryEventAndAwaitReceived(recipeInstanceId, sensoryEvent).get(); baker.awaitCompleted(recipeInstanceId, Duration.ofSeconds(5)).get(); diff --git a/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java b/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java index 57b211029..e98015552 100644 --- a/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java +++ b/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java @@ -6,7 +6,7 @@ public class WebShopVisualization { public void printVisualizationString() { - CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(WebShopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe); String graphvizString = compiledRecipe.getRecipeVisualization(); System.out.println(graphvizString); } diff --git a/examples/docs-code-snippets/src/main/kotlin/examples/kotlin/application/WebShopApp.kt b/examples/docs-code-snippets/src/main/kotlin/examples/kotlin/application/WebShopApp.kt index 10a9fbce4..ed2c7891a 100644 --- a/examples/docs-code-snippets/src/main/kotlin/examples/kotlin/application/WebShopApp.kt +++ b/examples/docs-code-snippets/src/main/kotlin/examples/kotlin/application/WebShopApp.kt @@ -11,7 +11,7 @@ import examples.kotlin.interactions.CheckStockImpl import examples.kotlin.interactions.ShipOrderImpl import examples.kotlin.recipes.WebShopRecipe import kotlinx.coroutines.runBlocking -import java.util.* +import java.util.UUID import kotlin.time.Duration.Companion.seconds @ExperimentalDsl diff --git a/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala b/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala index 494ea23ba..e0c5e0971 100644 --- a/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala +++ b/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala @@ -30,7 +30,7 @@ class WebShopApp { val sensoryEvent = EventInstance.unsafeFrom(orderPlaced) for { - recipeId <- bakerF.addRecipe(RecipeCompiler.compileRecipe(recipe = WebShopRecipe.recipe), validate = true) + recipeId <- bakerF.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe), validate = true) _ <- bakerF.bake(recipeId, recipeInstanceId) _ <- bakerF.fireSensoryEventAndAwaitReceived(recipeInstanceId, sensoryEvent) _ <- bakerF.awaitCompleted(recipeInstanceId, timeout = 5.seconds) diff --git a/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala b/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala index e37da969f..d318b41f3 100644 --- a/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala +++ b/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala @@ -5,7 +5,7 @@ import examples.scala.recipes.WebShopRecipe class WebShopVisualization { def printVisualizationString(): Unit = { - val compiledRecipe = RecipeCompiler.compileRecipe(WebShopRecipe.recipe) + val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe) val visualization = compiledRecipe.getRecipeVisualization println(visualization) } diff --git a/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java b/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java index 660416861..50c5f6935 100644 --- a/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java +++ b/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java @@ -9,7 +9,7 @@ public class WebShopRecipeTest { @Test public void recipeShouldCompileWithoutValidationErrors() { - var validationErrors = RecipeCompiler.compileRecipe(WebShopRecipe.recipe).getValidationErrors(); + var validationErrors = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe).getValidationErrors(); assertTrue( String.format("Recipe compilation resulted in validation errors: \n%s", validationErrors), validationErrors.isEmpty() diff --git a/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala b/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala index ef1220e99..71ce3b6e5 100644 --- a/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala +++ b/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala @@ -6,7 +6,7 @@ import org.scalatest.matchers.should.Matchers class WebShopRecipeTest extends AnyFlatSpec with Matchers { "Recipe" should "compile without validation errors" in { - val validationErrors = RecipeCompiler.compileRecipe(WebShopRecipe.recipe).validationErrors + val validationErrors = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe).validationErrors assert(validationErrors.isEmpty) } } diff --git a/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala b/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala index a1374e47a..f355aba29 100644 --- a/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala +++ b/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala @@ -26,7 +26,7 @@ class RecipeLoaderSpec extends AnyFunSuite with Matchers with BeforeAndAfterAll private val gzippedBase64RecipeFile = File.createTempFile("tmp", "recipe-gzipped-base64") override protected def beforeAll(): Unit = { - val recipe: CompiledRecipe = RecipeCompiler.compileRecipe(Recipe("Webshop") + val recipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(Recipe("Webshop") .withSensoryEvents( Event[OrderPlaced], Event[PaymentMade]) From fea6a388df34d1314ff496e5a7d876e39047b8c0 Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Fri, 3 Apr 2026 14:00:17 +0200 Subject: [PATCH 05/16] adding kotlin model classes --- .../baker/compiler/PreCompileValidations.kt | 12 +- .../com/ing/baker/compiler/RecipeCompiler.kt | 723 +++++++++++++++- .../baker/compiler/RecipeCompilerKotlin.kt | 799 ------------------ .../com/ing/baker/recipe/CheckPointEvent.kt | 8 + .../main/kotlin/com/ing/baker/recipe/Event.kt | 7 + .../baker/recipe/EventOutputTransformer.kt | 3 + .../kotlin/com/ing/baker/recipe/Ingredient.kt | 5 + .../{model/Model.kt => recipe/Interaction.kt} | 78 +- .../baker/{model => recipe}/KotlinBridge.kt | 19 +- .../kotlin/com/ing/baker/recipe/Recipe.kt | 16 + .../main/kotlin/com/ing/baker/recipe/Sieve.kt | 11 + .../baker/compiler/RecipeCompilerBasicTest.kt | 4 +- .../baker/compiler/RecipeCompilerDslTest.kt | 4 +- .../compiler/RecipeCompilerFeaturesTest.kt | 10 +- 14 files changed, 811 insertions(+), 888 deletions(-) delete mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Event.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt rename core/baker-compiler/src/main/kotlin/com/ing/baker/{model/Model.kt => recipe/Interaction.kt} (50%) rename core/baker-compiler/src/main/kotlin/com/ing/baker/{model => recipe}/KotlinBridge.kt (97%) create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Recipe.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Sieve.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt index 6824caf92..e15a3cc8d 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt @@ -1,10 +1,10 @@ package com.ing.baker.compiler -import com.ing.baker.model.Event -import com.ing.baker.model.Ingredient -import com.ing.baker.model.Interaction -import com.ing.baker.model.Recipe -import com.ing.baker.model.toKotlin +import com.ing.baker.recipe.Event +import com.ing.baker.recipe.Ingredient +import com.ing.baker.recipe.Interaction +import com.ing.baker.recipe.Recipe +import com.ing.baker.recipe.toKotlin import scala.jdk.javaapi.CollectionConverters import com.ing.baker.recipe.common.Recipe as ScalaRecipe @@ -46,7 +46,7 @@ object PreCompileValidations { private fun assertSensoryEventsNegativeFiringLimits(recipe: Recipe): List = recipe.sensoryEvents - .firstOrNull { it.maxFiringLimit != null && it.maxFiringLimit <= 0 } + .firstOrNull { it.maxFiringLimit != null && it.maxFiringLimit!! <= 0 } ?.let { listOf("MaxFiringLimit should be greater than 0") } ?: emptyList() private fun assertRequiredEventForReprovider(recipe: Recipe): List = diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt index c28d429ea..66919dc75 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt @@ -1,8 +1,86 @@ package com.ing.baker.compiler +import com.ing.baker.compiler.PreCompileValidations.preCompileAssertions +import com.ing.baker.compiler.ScalaConversions.asJava +import com.ing.baker.compiler.ScalaConversions.asScala import com.ing.baker.il.CompiledRecipe +import com.ing.baker.il.EventDescriptor +import com.ing.baker.il.IngredientDescriptor +import com.ing.baker.il.RecipeValidations import com.ing.baker.il.ValidationSettings +import com.ing.baker.il.failurestrategy.`BlockInteraction$` +import com.ing.baker.il.failurestrategy.FireEventAfterFailure +import com.ing.baker.il.failurestrategy.FireFunctionalEventAfterFailure +import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff +import com.ing.baker.il.`package$` +import com.ing.baker.il.petrinet.Edge +import com.ing.baker.il.petrinet.EventTransition +import com.ing.baker.il.petrinet.InteractionTransition +import com.ing.baker.il.petrinet.IntermediateTransition +import com.ing.baker.il.petrinet.MissingEventTransition +import com.ing.baker.il.petrinet.MultiFacilitatorTransition +import com.ing.baker.il.petrinet.Place +import com.ing.baker.il.petrinet.Place.FiringLimiterPlace +import com.ing.baker.il.petrinet.Transition +import com.ing.baker.petrinet.api.PetriNet +import com.ing.baker.recipe.common.CheckPointEvent +import com.ing.baker.recipe.common.Event +import com.ing.baker.recipe.common.EventOutputTransformer +import com.ing.baker.recipe.common.Ingredient +import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.common.InteractionFailureStrategy import com.ing.baker.recipe.common.Recipe +import com.ing.baker.recipe.common.Sieve +import com.ing.baker.recipe.scaladsl.`Event$` +import com.ing.baker.recipe.scaladsl.Interaction +import com.ing.baker.types.`NullValue$` +import com.ing.baker.types.OptionType +import com.ing.baker.types.Type +import com.ing.baker.types.Value +import scala.Option +import scala.Some +import scala.collection.immutable.Seq +import scala.reflect.ClassTag +import scala.util.Either +import scala.util.Left +import scala.util.Right +import scalax.collection.Graph +import scalax.collection.`Graph$` +import scalax.collection.GraphEdge +import scalax.collection.config.CoreConfig +import scalax.collection.config.GraphConfig +import scalax.collection.edge.WLDiEdge +import scalax.collection.edge.`WLDiEdge$` +import scalax.collection.mutable.ArraySet +import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` as Scala212CompatibleScala +import com.ing.baker.il.failurestrategy.InteractionFailureStrategy as ILInteractionFailureStrategy +import com.ing.baker.il.`package$`.`MODULE$` as ILPackage +import com.ing.baker.il.petrinet.Place.`EmptyEventIngredientPlace$`.`MODULE$` as EmptyEventIngredientPlace +import com.ing.baker.il.petrinet.Place.`EventPreconditionPlace$`.`MODULE$` as EventPreconditionPlace +import com.ing.baker.il.petrinet.Place.`IngredientPlace$`.`MODULE$` as IngredientPlace +import com.ing.baker.il.petrinet.Place.`InteractionEventOutputPlace$`.`MODULE$` as InteractionEventOutputPlace +import com.ing.baker.il.petrinet.Place.`IntermediatePlace$`.`MODULE$` as IntermediatePlace +import com.ing.baker.il.petrinet.Place.`MultiTransitionPlace$`.`MODULE$` as MultiTransitionPlace + +/** + * Type alias for the node type of the scalax.collection.Graph backing the petri net. + */ +typealias Node = Either + +/** + * Type alias for the edge type of the scalax.collection.Graph backing the petri net. + */ +typealias Arc = WLDiEdge + +/** + * Type alias for a multi set. + */ +typealias MultiSet = Map + +/** + * Type alias for a marking. + */ +typealias Marking = Map> object RecipeCompiler { @@ -22,6 +100,647 @@ object RecipeCompiler { * @param validationSettings The validation settings to follow for the validation * @return */ - fun compileRecipe(recipe: Recipe, validationSettings: ValidationSettings): CompiledRecipe = - RecipeCompilerKotlin.compileRecipe(recipe, validationSettings) + fun compileRecipe( + recipe: Recipe, + validationSettings: ValidationSettings + ): CompiledRecipe { + + val precompileErrors: List = preCompileAssertions(recipe).asJava + + // Extend the interactions with the checkpoint event interactions and sub-recipes + val actionDescriptors: List = recipe.interactions + + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + + recipe.sieves.map(::convertSieveToInteraction) + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + val sensoryEvents = flattenSensoryEvents(recipe) + + //All ingredient names provided by sensory events or by interactions + val allIngredientNames: Set = + sensoryEvents + .flatMap { e -> e.providedIngredients.map(Ingredient::name) } + .toSet() + + actionDescriptors.flatMap { i -> + i.output.flatMap { e -> + // check if the event was renamed (check if there is a transformer for this event) + when (val transformer = i.eventOutputTransformers[e]) { + null -> e.providedIngredients.map(Ingredient::name) + else -> e.providedIngredients.map { ingredient -> + transformer.ingredientRenames[ingredient.name()] ?: ingredient.name() + } + } + } + } + + // For inputs for which no matching output cannot be found, we do not want to generate a place. + // It should be provided at runtime from outside the active petri net (marking) + val allInteractionTransitions: List = + actionDescriptors.map { interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) } + + // events provided from outside + val sensoryEventTransitions: List = sensoryEvents.map { event -> + EventTransition( + EventDescriptor( + event.name(), + event.providedIngredients().map { IngredientDescriptor(it.name(), it.ingredientType()) } as Seq + ), true, event.maxFiringLimit() + ) + } + + // events provided by other transitions / actions + val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire.map { event -> EventTransition(event, false, Option.empty()) } + } + + val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions + + // Given the event classes, it is creating the ingredient places and + // connecting a transition to a ingredient place. + val internalEventArcs: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire.flatMap { event -> + event.ingredients.map { ingredient -> + val from = + interactionEventTransitions.find { it.label() == event.name() } + ?: throw RecipeValidationException() + arc(from, createPlace(ingredient.name(), IngredientPlace)) + } + } + } + + //Create event limiter places so that events can only fire x amount of times. + val eventLimiterArcs: List = sensoryEventTransitions.flatMap { t -> + when (val n = t.maxFiringLimit().getOrElse { null as Int? }) { + null -> emptyList() + else -> listOf(arc(createPlace("limit:${t.label()}", FiringLimiterPlace(n)), t)) + } + } + + fun findEventTransitionByEventName(eventName: String) = + allEventTransitions.find { it.event().name() == eventName } + + fun findInteractionByLabel(label: String) = + allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() + + // This generates precondition arcs for Required Events (AND). + val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t -> + buildEventAndPreconditionArcs( + t, + ::findEventTransitionByEventName, + ::findInteractionByLabel + ) + }.unzipFlatten() + + // This generates precondition arcs for Required Events (OR). + val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t -> + buildEventORPreconditionArcs(t, ::findEventTransitionByEventName, ::findInteractionByLabel) + }.unzipFlatten() + + val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } + + // It connects a sensory event to an ingredient places + val sensoryEventArcs: List = sensoryEventWithIngredients.flatMap { et -> + et.event().ingredients.map { ingredient -> + arc(et, createPlace(ingredient.name(), IngredientPlace)) + } + } + + val eventThatArePreconditions: List = actionDescriptors.flatMap { actionDescriptor -> + actionDescriptor.requiredEvents + actionDescriptor.requiredOneOfEvents.flatten() + } + + // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net + val sensoryEventArcsNoIngredientsArcs: List = sensoryEventWithoutIngredients + //Filter out events that are preconditions to interactions + .filterNot { sensoryEvent -> eventThatArePreconditions.contains(sensoryEvent.label()) } + .map { sensoryEvent -> + arc(sensoryEvent, createPlace(sensoryEvent.label(), EmptyEventIngredientPlace)) + } + + // First find the cases where multiple transitions depend on the same ingredient place + val ingredientsWithMultipleConsumers: Map> = + getIngredientsWithMultipleConsumers(allInteractionTransitions) + + // Add one new transition for each duplicate input (the newly added one in the image above) + val multipleConsumerFacilitatorTransitions: List = + ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) + + val multipleOutputFacilitatorArcs: List = + multipleConsumerFacilitatorTransitions.map { t -> + arc(createPlace(t.label(), IngredientPlace), t) + } + + val interactionArcs: List = + allInteractionTransitions.flatMap { interactionTransition -> + buildInteractionArcs( + multipleConsumerFacilitatorTransitions, + ingredientsWithMultipleConsumers, + interactionEventTransitions, + interactionTransition + ) + } + + val arcs = (interactionArcs + + eventPreconditionArcs + + eventOrPreconditionArcs + + eventLimiterArcs + + sensoryEventArcs + + sensoryEventArcsNoIngredientsArcs + + internalEventArcs + + multipleOutputFacilitatorArcs) + + val petriNet = PetriNet(graph(arcs)) + + val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> + when (val placeType = p.placeType()) { + is FiringLimiterPlace -> p to mapOf(null to placeType.maxLimit()) + else -> null + } + }.toMap() + + val errors = preconditionORErrors + preconditionANDErrors + precompileErrors + + val compiledRecipe = CompiledRecipe.build( + recipe.name(), + petriNet, + initialMarking.mapValues { it.value.mapValues { it.value as Any }.asScala }.asScala, + errors.asScala, + recipe.eventReceivePeriod(), + recipe.retentionPeriod(), + Scala212CompatibleScala, + ) + + return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) + } + + fun List, List>>.unzipFlatten() = this.unzip().let { pair -> + pair.first.flatten() to pair.second.flatten() + } + + private fun transition(transition: Transition) = Right(transition) + private fun place(place: Place) = Left(place) + + private fun arc(t: Transition, p: Place): Arc = + wlDiEdge(transition(t), place(p), Edge(Option.empty())) + + private fun arc(p: Place, t: Transition, eventFilter: String? = null): Arc = + wlDiEdge(place(p), transition(t), Edge(Option.apply(eventFilter))) + + /** + * Creates a transition for a missing event in the recipe. + */ + private fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) + + private fun buildEventAndPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = + // Find the event in available events + interaction.requiredEvents.map { eventName -> + // a new `Place` generated for each AND events + val eventPreconditionPlace = + createPlace( + label = "$eventName-${interaction.name()}", + placeType = EventPreconditionPlace + ) + buildEventPreconditionArcs( + eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name()) + ) + }.unzipFlatten() + + private fun buildEventORPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = interaction.requiredOneOfEvents.mapIndexed { index: Int, orGroup: Set -> + // only one `Place` for all the OR events + val eventPreconditionPlace = createPlace( + label = "${interaction.name()}-or-$index", + placeType = EventPreconditionPlace + ) + orGroup.map { eventName -> + buildEventPreconditionArcs( + eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name()) + ) + }.unzipFlatten() + }.unzipFlatten() + + private fun buildEventPreconditionArcs( + eventName: String, + preconditionPlace: Place, + preconditionTransition: (String) -> Transition?, + interactionTransition: Transition + ): Pair, List> { + + val eventTransition = preconditionTransition(eventName) + + val notProvidedError = when (eventTransition) { + null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") + else -> emptyList() + } + + val arcs = listOf( + arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace), + arc(preconditionPlace, interactionTransition) + ) + + return arcs to notProvidedError + } + + // the (possible) event output arcs / places + private fun buildInteractionOutputArcs( + interaction: InteractionTransition, + eventTransitions: List + ) = + if (interaction.eventsToFire.isNotEmpty()) { + val resultPlace = + createPlace(label = interaction.label(), placeType = InteractionEventOutputPlace) + val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor -> + //Get the correct event transition + val eventTransition = eventTransitions.find { it.event().name() == event.name() } + ?: throw RecipeValidationException("eventTransition should be found") + //Decide if there are multiple interactions that fire this transition, + // if so create a event combiner place + // else link the transition to the event. + val eventTransitionCount = eventTransitions.count { e -> e.event().name() == event.name() } + if (eventTransitionCount > 1) { + //Create a new intermediate event place + val eventCombinerPlace: Place = + createPlace(label = event.name(), placeType = IntermediatePlace) + //Create a new intermediate event transition + val interactionToEventTransition: IntermediateTransition = + IntermediateTransition("${interaction.interactionName()}:${event.name()}") + //link the interaction output place to the intermediate transition + val interactionOutputPlaceToIntermediateTransition: Arc = + arc(resultPlace, interactionToEventTransition, event.name()) + //link the intermediate transition to the intermediate input place + val intermediateTransitionToEventCombinerPlace: Arc = + arc(interactionToEventTransition, eventCombinerPlace) + //Link the intermediate place to the event place + val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition) + listOf( + intermediateTransitionToEventCombinerPlace, + interactionOutputPlaceToIntermediateTransition, + eventCombinerPlaceToEventTransition + ) + } else { + listOf(arc(resultPlace, eventTransition, event.name())) + } + } + eventArcs + arc(interaction, resultPlace) + } else emptyList() + + /** + * Draws an arc from all required ingredients for an interaction + * If the ingredient has multiple consumers create a multi transition place and create both arcs for it + */ + private fun buildInteractionInputArcs( + t: InteractionTransition, + multipleConsumerFacilitatorTransitions: List, + ingredientsWithMultipleConsumers: Map> + ): List { + + val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = + t.nonProvidedIngredients.map(IngredientDescriptor::name) + .partition(ingredientsWithMultipleConsumers::contains) + + // the extra arcs to model multiple output transitions from one place + val internalDataInputArcs = fieldNamesWithPrefixMulti.flatMap { fieldName -> + val multiTransitionPlace = + createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace) + listOf( + // one arc from multiplier place to the transition + arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), multiTransitionPlace), + // one arc from extra added place to transition + arc(multiTransitionPlace, t) + ) + } + + // the data input arcs / places + val dataInputArcs: List = fieldNamesWithoutPrefix.map { fieldName -> + arc(createPlace(fieldName, IngredientPlace), t) + } + + val dataOutputArcs: List = + if (t.isReprovider) + fieldNamesWithoutPrefix.map { fieldName -> + arc(t, createPlace(fieldName, IngredientPlace)) + } + fieldNamesWithPrefixMulti.map { fieldName -> + arc(t, createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace)) + } + else + emptyList() + + val limitInteractionCountArc: List = when (val maximumInteractionCount = t.maximumInteractionCount) { + null -> emptyList() + else -> listOf( + arc(createPlace("limit:${t.label()}", FiringLimiterPlace(maximumInteractionCount)), t) + ) + } + + return dataInputArcs + dataOutputArcs + internalDataInputArcs + limitInteractionCountArc + } + + private fun buildInteractionArcs( + multipleOutputFacilitatorTransitions: List, + placeNameWithDuplicateTransitions: Map>, + eventTransitions: List, interactionTransition: InteractionTransition + ) = + buildInteractionInputArcs( + interactionTransition, + multipleOutputFacilitatorTransitions, + placeNameWithDuplicateTransitions + ) + buildInteractionOutputArcs( + interactionTransition, + eventTransitions + ) + + private fun getMultiTransition(internalRepresentationName: String, transitions: List) = + transitions.find { it.label().equals(internalRepresentationName) } + ?: throw NoSuchElementException("No multi transition found with name $internalRepresentationName") + + /** + * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. + * + * @param actionTransitions Seq of reflected transition. + * @return A map from input place name to reflected transitions (where the transitions have as input the place). + */ + private fun getIngredientsWithMultipleConsumers(actionTransitions: List): Map> = + // Obtain a list of field name with their transition + actionTransitions + .flatMap { transition -> + transition.nonProvidedIngredients.map { ingredient -> + ingredient.name() to transition + } + } + .groupBy({ it.first }, { it.second }) + // Only keep those place names which have more than one out-adjacent transition + .filter { (_, interactions) -> interactions.size >= 2 } + + private fun createPlace(label: String, placeType: Place.PlaceType): Place = + Place("${placeType.labelPrepend()}$label", placeType) + + private fun convertCheckpointEventToInteraction(e: CheckPointEvent) = + interaction( + name = "${`package$`.`MODULE$`.checkpointEventInteractionPrefix()}${e.name()}", + inputIngredients = emptyList(), + output = listOf(`Event$`.`MODULE$`.apply(e.name(), emptySequence().asScala)), + requiredEvents = e.requiredEvents, + requiredOneOfEvents = e.requiredOneOfEvents + ) + + private fun convertSieveToInteraction(s: Sieve) = + interaction( + name = "${`package$`.`MODULE$`.sieveInteractionPrefix()}${s.name()}", + inputIngredients = s.inputIngredients, + output = s.output, + ) + + private fun flattenSubRecipesToInteraction(recipe: Recipe): Set { + fun copyInteraction(i: InteractionDescriptor) = Interaction.apply( + $$"$${`package$`.`MODULE$`.subRecipePrefix()}$${recipe.name()}$$${i.name()}", + i.inputIngredients(), + i.output(), + i.requiredEvents(), + i.requiredOneOfEvents(), + i.predefinedIngredients(), + i.overriddenIngredientNames(), + i.overriddenOutputIngredientName(), + i.maximumInteractionCount(), + i.failureStrategy(), + i.eventOutputTransformers(), + i.isReprovider, + Option.apply(i.originalName()) + ) + return recipe.interactions.map(::copyInteraction).toSet() + + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + recipe.sieves.map(::convertSieveToInteraction) + } + + private fun flattenSensoryEvents(recipe: Recipe): Set = + recipe.sensoryEvents + recipe.subRecipes.flatMap(::flattenSensoryEvents) + + private fun graph(arcs: List): Graph> = + `Graph$`.`MODULE$`.from( + arcs.map { it as WLDiEdge }.asScala, + ClassTag.apply(WLDiEdge::class.java), + CoreConfig(GraphConfig.defaultOrder(), ArraySet.`Hints$`.`MODULE$`.apply(16, 32, 48, 80)) + ) as Graph> + + private fun wlDiEdge(node1: Node, node2: Node, label: L): WLDiEdge = + `WLDiEdge$`.`MODULE$`.newEdge(GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), 1.0, label) + + private fun interactionTransitionOf( + interactionDescriptor: InteractionDescriptor, + defaultFailureStrategy: InteractionFailureStrategy, + allIngredientNames: Set + ): InteractionTransition { + //This transforms the event using the eventOutputTransformer to the new event + //If there is no eventOutputTransformer for the event the original event is returned + fun transformEventType(event: Event): Event = + when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers[event]) { + null -> event + else -> com.ing.baker.recipe.scaladsl.Event( + eventOutputTransformer.newEventName, + event.providedIngredients.map { i -> + Ingredient( + eventOutputTransformer.ingredientRenames.getOrElse(i.name(), { i.name() }), + i.ingredientType() + ) + }.asScala, + Option.empty() + ) + } + + fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = + com.ing.baker.il.EventOutputTransformer( + recipeEventOutputTransformer.newEventName(), + recipeEventOutputTransformer.ingredientRenames() + ) + + fun transformEventToCompiledEvent(event: Event): EventDescriptor = + EventDescriptor( + event.name(), + event.providedIngredients().map { IngredientDescriptor(it.name(), it.ingredientType()) } as Seq + ) + + // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe + // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe + // Replace BakerEventList to BakerEventList tag as know in compiledRecipe + // Replace ingredient tags with overridden tags + val inputFields: Seq> = interactionDescriptor.inputIngredients + .map { ingredient -> + when (ingredient.name()) { + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> ILPackage.recipeInstanceIdName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> ILPackage.recipeInstanceMetadataName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> ILPackage.recipeInstanceEventListName() + else -> interactionDescriptor.overriddenIngredientNames() + .getOrElse(ingredient.name(), { ingredient.name() }) + } to ingredient.ingredientType() + }.asScala + + val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala + val eventsToFire = + interactionDescriptor.output.map(::transformEventType).map(::transformEventToCompiledEvent).asScala + + //For each ingredient that is not provided + //And is of the type Optional or Option + //Add it to the predefinedIngredients List as empty + //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. + val predefinedIngredientsWithOptionalsEmpty: Map = + inputFields.asJava + .filter { (name, type) -> type is OptionType && name !in allIngredientNames } + .associate { Pair(it.first, `NullValue$`.`MODULE$`) } + + interactionDescriptor.predefinedIngredients + + val p: Triple, Option> = + when (val strategy = interactionDescriptor.failureStrategy().getOrElse { defaultFailureStrategy }) { + is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { + val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { + is Some -> Some( + EventDescriptor( + e.value().getOrElse { null as String? } + ?: (interactionDescriptor.name() + ILPackage.exhaustedEventAppend()), + emptyList().asScala) + ) + + else -> Option.empty() + } + val functionalFailedEvent = when (val e = strategy.fireFunctionalEvent()) { + is Some -> Some( + EventDescriptor( + e.value().getOrElse { null as String? } + ?: (interactionDescriptor.name() + ILPackage.functionalFailedEventAppend()), + emptyList().asScala) + ) + + else -> Option.empty() + } + Triple( + RetryWithIncrementalBackoff( + strategy.initialDelay(), + strategy.backoffFactor(), + strategy.maximumRetries(), + strategy.maxTimeBetweenRetries(), + exhaustedRetryEvent, + functionalFailedEvent, + ), exhaustedRetryEvent, functionalFailedEvent + ) + } + + is InteractionFailureStrategy.BlockInteraction -> Triple( + `BlockInteraction$`.`MODULE$`, + Option.empty(), + Option.empty() + ) + + is InteractionFailureStrategy.FireEventAfterFailure -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } + val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) + Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) + } + + is InteractionFailureStrategy.FireEventAndBlock -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } + val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) + Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) + } + + is InteractionFailureStrategy.FireEventAndResolve -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name() + ILPackage.functionalFailedEventAppend() } + val functionalFailed = EventDescriptor(eventName, emptyList().asScala) + Triple(FireFunctionalEventAfterFailure(functionalFailed), Option.empty(), Some(functionalFailed)) + } + + else -> Triple(`BlockInteraction$`.`MODULE$`, Option.empty(), Option.empty()) + } + val failureStrategy = p.first + val exhaustedRetryEvent = p.second.toList().toSeq() + val functionalRetryEvent = p.third.toList().toSeq() + + val eventsToFireAll = eventsToFire.asJava + exhaustedRetryEvent.asJava + functionalRetryEvent.asJava + val originalEventsAll = originalEvents.asJava + exhaustedRetryEvent.asJava + functionalRetryEvent.asJava + + return InteractionTransition( + eventsToFireAll.asScala, + originalEventsAll.asScala, + inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala, + interactionDescriptor.name(), + interactionDescriptor.originalName(), + predefinedIngredientsWithOptionalsEmpty.asScala, + interactionDescriptor.maximumInteractionCount(), + failureStrategy, + interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> + event.name() to transformEventOutputTransformer( + transformer + ) + }.toMap().asScala, + interactionDescriptor.isReprovider() + ) + } + + private fun interaction( + name: String?, + inputIngredients: List, + output: List, + requiredEvents: Set = emptySet(), + requiredOneOfEvents: Set> = emptySet(), + predefinedIngredients: Map = emptyMap(), + overriddenIngredientNames: Map = emptyMap(), + overriddenOutputIngredientName: String? = null, + maximumInteractionCount: Int? = null, + failureStrategy: InteractionFailureStrategy? = null, + eventOutputTransformers: Map = emptyMap(), + isReprovider: Boolean = false, + oldName: String? = null, + ) = + Interaction.apply( + name, + inputIngredients.asScala, + output.asScala, + requiredEvents.asScala, + requiredOneOfEvents.map { it: Set -> it.asScala }.toSet().asScala, + predefinedIngredients.asScala, + overriddenIngredientNames.asScala, + Option.apply(overriddenOutputIngredientName), + Option.apply(maximumInteractionCount), + Option.apply(failureStrategy), + eventOutputTransformers.asScala, + isReprovider, + Option.apply(oldName) + ) } + +val Recipe.sensoryEvents get() = this.sensoryEvents().asJava +val Recipe.interactions get() = this.interactions().asJava +val Recipe.subRecipes get() = this.subRecipes().asJava +val Recipe.checkpointEvents get() = this.checkpointEvents().asJava +val Recipe.sieves get() = this.sieves().asJava +val InteractionDescriptor.requiredEvents get() = this.requiredEvents().asJava +val InteractionDescriptor.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() +val InteractionDescriptor.output get() = this.output().asJava +val InteractionDescriptor.eventOutputTransformers get() = this.eventOutputTransformers().asJava +val InteractionDescriptor.inputIngredients get() = this.inputIngredients().asJava +val InteractionDescriptor.predefinedIngredients get() = this.predefinedIngredients().asJava +val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava +val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava +val InteractionTransition.maximumInteractionCount + get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } +val Event.providedIngredients get() = this.providedIngredients().asJava +val EventDescriptor.ingredients get() = this.ingredients().asJava +val EventOutputTransformer.ingredientRenames get() = this.ingredientRenames().asJava +val EventOutputTransformer.newEventName: String? get() = this.newEventName() +val CheckPointEvent.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() +val CheckPointEvent.requiredEvents get() = this.requiredEvents().asJava +val Sieve.inputIngredients get() = this.inputIngredients().asJava +val Sieve.output get() = this.output().asJava diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt deleted file mode 100644 index e3d89285e..000000000 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt +++ /dev/null @@ -1,799 +0,0 @@ -package com.ing.baker.compiler - -import com.ing.baker.compiler.PreCompileValidations.preCompileAssertions -import com.ing.baker.compiler.ScalaConversions.asJava -import com.ing.baker.compiler.ScalaConversions.asScala -import com.ing.baker.il.CompiledRecipe -import com.ing.baker.il.CompiledRecipe.OldRecipeIdVariant -import com.ing.baker.il.EventDescriptor -import com.ing.baker.il.IngredientDescriptor -import com.ing.baker.il.RecipeValidations -import com.ing.baker.il.ValidationSettings -import com.ing.baker.il.failurestrategy.`BlockInteraction$` -import com.ing.baker.il.failurestrategy.FireEventAfterFailure -import com.ing.baker.il.failurestrategy.FireFunctionalEventAfterFailure -import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff -import com.ing.baker.il.`package$` -import com.ing.baker.il.petrinet.Edge -import com.ing.baker.il.petrinet.EventTransition -import com.ing.baker.il.petrinet.InteractionTransition -import com.ing.baker.il.petrinet.IntermediateTransition -import com.ing.baker.il.petrinet.MissingEventTransition -import com.ing.baker.il.petrinet.MultiFacilitatorTransition -import com.ing.baker.il.petrinet.Place -import com.ing.baker.il.petrinet.Place.FiringLimiterPlace -import com.ing.baker.il.petrinet.Transition -import com.ing.baker.petrinet.api.PetriNet -import com.ing.baker.recipe.common.CheckPointEvent -import com.ing.baker.recipe.common.Event -import com.ing.baker.recipe.common.EventOutputTransformer -import com.ing.baker.recipe.common.Ingredient -import com.ing.baker.recipe.common.InteractionDescriptor -import com.ing.baker.recipe.common.InteractionFailureStrategy -import com.ing.baker.recipe.common.Recipe -import com.ing.baker.recipe.common.Sieve -import com.ing.baker.recipe.scaladsl.`Event$` -import com.ing.baker.recipe.scaladsl.Interaction -import com.ing.baker.types.`NullValue$` -import com.ing.baker.types.OptionType -import com.ing.baker.types.Type -import com.ing.baker.types.Value -import scala.Option -import scala.Some -import scala.collection.immutable.Seq -import scala.reflect.ClassTag -import scala.util.Either -import scala.util.Left -import scala.util.Right -import scalax.collection.Graph -import scalax.collection.`Graph$` -import scalax.collection.GraphEdge -import scalax.collection.config.CoreConfig -import scalax.collection.config.GraphConfig -import scalax.collection.edge.WLDiEdge -import scalax.collection.edge.`WLDiEdge$` -import scalax.collection.mutable.ArraySet -import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleJava$`.`MODULE$` as Scala212CompatibleJava -import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleKotlin$`.`MODULE$` as Scala212CompatibleKotlin -import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` as Scala212CompatibleScala -import com.ing.baker.il.failurestrategy.InteractionFailureStrategy as ILInteractionFailureStrategy -import com.ing.baker.il.`package$`.`MODULE$` as ILPackage -import com.ing.baker.il.petrinet.Place.`EmptyEventIngredientPlace$`.`MODULE$` as EmptyEventIngredientPlace -import com.ing.baker.il.petrinet.Place.`EventPreconditionPlace$`.`MODULE$` as EventPreconditionPlace -import com.ing.baker.il.petrinet.Place.`IngredientPlace$`.`MODULE$` as IngredientPlace -import com.ing.baker.il.petrinet.Place.`InteractionEventOutputPlace$`.`MODULE$` as InteractionEventOutputPlace -import com.ing.baker.il.petrinet.Place.`IntermediatePlace$`.`MODULE$` as IntermediatePlace -import com.ing.baker.il.petrinet.Place.`MultiTransitionPlace$`.`MODULE$` as MultiTransitionPlace -import com.ing.baker.recipe.javadsl.Recipe as JavaRecipe -import com.ing.baker.recipe.kotlindsl.Recipe as KotlinRecipe -import com.ing.baker.recipe.scaladsl.Recipe as ScalaRecipe - -/** - * Type alias for the node type of the scalax.collection.Graph backing the petri net. - */ -typealias Node = Either - -/** - * Type alias for the edge type of the scalax.collection.Graph backing the petri net. - */ -typealias Arc = WLDiEdge - -/** - * Type alias for a multi set. - */ -typealias MultiSet = Map - -/** - * Type alias for a marking. - */ -typealias Marking = Map> - - -object RecipeCompilerKotlin { - fun List, List>>.unzipFlatten() = this.unzip().let { pair -> - pair.first.flatten() to pair.second.flatten() - } - - fun transition(transition: Transition) = Right(transition) - fun place(place: Place) = Left(place) - - fun arc(t: Transition, p: Place, weight: Long): Arc = - wlDiEdge(transition(t), place(p), weight.toDouble(), Edge(Option.empty())) - - fun arc(p: Place, t: Transition, weight: Long, eventFilter: String? = null): Arc = - wlDiEdge(place(p), transition(t), weight.toDouble(), Edge(Option.apply(eventFilter))) - - /** - * Creates a transition for a missing event in the recipe. - */ - private fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) - - private fun buildEventAndPreconditionArcs( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition - ) = - // Find the event in available events - interaction.requiredEvents.map { eventName -> - // a new `Place` generated for each AND events - val eventPreconditionPlace = - createPlace( - label = "$eventName-${interaction.name()}", - placeType = EventPreconditionPlace - ) - buildEventPreconditionArcs( - eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name()) - ) - }.unzipFlatten() - - private fun buildEventORPreconditionArcs( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition - ) = interaction.requiredOneOfEvents.mapIndexed { index: Int, orGroup: Set -> - // only one `Place` for all the OR events - val eventPreconditionPlace = createPlace( - label = "${interaction.name()}-or-$index", - placeType = EventPreconditionPlace - ) - orGroup.map { eventName -> - buildEventPreconditionArcs( - eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name()) - ) - }.unzipFlatten() - }.unzipFlatten() - - private fun buildEventPreconditionArcs( - eventName: String, - preconditionPlace: Place, - preconditionTransition: (String) -> Transition?, - interactionTransition: Transition - ): Pair, List> { - - val eventTransition = preconditionTransition(eventName) - - val notProvidedError = when (eventTransition) { - null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") - else -> emptyList() - } - - val arcs = listOf( - arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace, 1), - arc(preconditionPlace, interactionTransition, 1) - ) - - return arcs to notProvidedError - } - - // the (possible) event output arcs / places - private fun buildInteractionOutputArcs( - interaction: InteractionTransition, - eventTransitions: List - ) = - if (interaction.eventsToFire.isNotEmpty()) { - val resultPlace = - createPlace(label = interaction.label(), placeType = InteractionEventOutputPlace) - val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor -> - //Get the correct event transition - val eventTransition = eventTransitions.find { it.event().name() == event.name() } - ?: throw RecipeValidationException("eventTransition should be found") - //Decide if there are multiple interactions that fire this transition, - // if so create a event combiner place - // else link the transition to the event. - val eventTransitionCount = eventTransitions.count { e -> e.event().name() == event.name() } - if (eventTransitionCount > 1) { - //Create a new intermediate event place - val eventCombinerPlace: Place = - createPlace(label = event.name(), placeType = IntermediatePlace) - //Create a new intermediate event transition - val interactionToEventTransition: IntermediateTransition = - IntermediateTransition("${interaction.interactionName()}:${event.name()}") - //link the interaction output place to the intermediate transition - val interactionOutputPlaceToIntermediateTransition: Arc = - arc(resultPlace, interactionToEventTransition, 1, event.name()) - //link the intermediate transition to the intermediate input place - val intermediateTransitionToEventCombinerPlace: Arc = - arc(interactionToEventTransition, eventCombinerPlace, 1) - //Link the intermediate place to the event place - val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) - listOf( - intermediateTransitionToEventCombinerPlace, - interactionOutputPlaceToIntermediateTransition, - eventCombinerPlaceToEventTransition - ) - } else { - listOf(arc(resultPlace, eventTransition, 1, event.name())) - } - } - eventArcs + arc(interaction, resultPlace, 1) - } else emptyList() - - /** - * Draws an arc from all required ingredients for an interaction - * If the ingredient has multiple consumers create a multi transition place and create both arcs for it - */ - private fun buildInteractionInputArcs( - t: InteractionTransition, - multipleConsumerFacilitatorTransitions: List, - ingredientsWithMultipleConsumers: Map> - ): List { - - val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = - t.nonProvidedIngredients.map(IngredientDescriptor::name) - .partition(ingredientsWithMultipleConsumers::contains) - - // the extra arcs to model multiple output transitions from one place - val internalDataInputArcs = fieldNamesWithPrefixMulti.flatMap { fieldName -> - val multiTransitionPlace = - createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace) - listOf( - // one arc from multiplier place to the transition - arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), multiTransitionPlace, 1), - // one arc from extra added place to transition - arc(multiTransitionPlace, t, 1) - ) - } - - // the data input arcs / places - val dataInputArcs: List = fieldNamesWithoutPrefix.map { fieldName -> - arc(createPlace(fieldName, IngredientPlace), t, 1) - } - - val dataOutputArcs: List = - if (t.isReprovider) - fieldNamesWithoutPrefix.map { fieldName -> - arc(t, createPlace(fieldName, IngredientPlace), 1) - } + fieldNamesWithPrefixMulti.map { fieldName -> - arc(t, createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace), 1) - } - else - emptyList() - - val limitInteractionCountArc: List = when (val maximumInteractionCount = t.maximumInteractionCount) { - null -> emptyList() - else -> listOf( - arc(createPlace("limit:${t.label()}", FiringLimiterPlace(maximumInteractionCount)), t, 1) - ) - } - - return dataInputArcs + dataOutputArcs + internalDataInputArcs + limitInteractionCountArc - } - - private fun buildInteractionArcs( - multipleOutputFacilitatorTransitions: List, - placeNameWithDuplicateTransitions: Map>, - eventTransitions: List, interactionTransition: InteractionTransition - ) = - buildInteractionInputArcs( - interactionTransition, - multipleOutputFacilitatorTransitions, - placeNameWithDuplicateTransitions - ) + buildInteractionOutputArcs( - interactionTransition, - eventTransitions - ) - - /** - * Compile the given recipe to a technical recipe that is useful for Baker. - * - * @param recipe ; The Recipe to compile and execute - * @param validationSettings The validation settings to follow for the validation - * @return - */ - fun compileRecipe( - recipe: Recipe, - validationSettings: ValidationSettings - ): CompiledRecipe { - - val precompileErrors: List = preCompileAssertions(recipe).asJava - - // Extend the interactions with the checkpoint event interactions and sub-recipes - val actionDescriptors: List = recipe.interactions + - recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + - recipe.sieves.map(::convertSieveToInteraction) + - recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) - - val sensoryEvents = flattenSensoryEvents(recipe) - - //All ingredient names provided by sensory events or by interactions - val allIngredientNames: Set = - sensoryEvents - .flatMap { e -> e.providedIngredients.map(Ingredient::name) } - .toSet() + - actionDescriptors.flatMap { i -> - i.output.flatMap { e -> - // check if the event was renamed (check if there is a transformer for this event) - when (val transformer = i.eventOutputTransformers[e]) { - null -> e.providedIngredients.map(Ingredient::name) - else -> e.providedIngredients.map { ingredient -> - transformer.ingredientRenames[ingredient.name()] ?: ingredient.name() - } - } - } - } - - // For inputs for which no matching output cannot be found, we do not want to generate a place. - // It should be provided at runtime from outside the active petri net (marking) - val allInteractionTransitions: List = - actionDescriptors.map { interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) } - - // events provided from outside - val sensoryEventTransitions: List = sensoryEvents.map { event -> - EventTransition(eventToCompiledEvent(event), true, event.maxFiringLimit()) - } - - // events provided by other transitions / actions - val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> - t.eventsToFire.map { event -> EventTransition(event, false, Option.empty()) } - } - - val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions - - // Given the event classes, it is creating the ingredient places and - // connecting a transition to a ingredient place. - val internalEventArcs: List = allInteractionTransitions.flatMap { t -> - t.eventsToFire.flatMap { event -> - event.ingredients.map { ingredient -> - val from = - interactionEventTransitions.find { it.label() == event.name() } - ?: throw RecipeValidationException() - arc(from, createPlace(ingredient.name(), IngredientPlace), 1) - } - } - } - - //Create event limiter places so that events can only fire x amount of times. - val eventLimiterArcs: List = sensoryEventTransitions.flatMap { t -> - when (val n = t.maxFiringLimit().getOrElse { null as Int? }) { - null -> emptyList() - else -> listOf(arc(createPlace("limit:${t.label()}", FiringLimiterPlace(n)), t, 1)) - } - } - - fun findEventTransitionByEventName(eventName: String) = - allEventTransitions.find { it.event().name() == eventName } - - fun findInteractionByLabel(label: String) = - allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() - - // This generates precondition arcs for Required Events (AND). - val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t -> - buildEventAndPreconditionArcs( - t, - ::findEventTransitionByEventName, - ::findInteractionByLabel - ) - }.unzipFlatten() - - // This generates precondition arcs for Required Events (OR). - val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t -> - buildEventORPreconditionArcs(t, ::findEventTransitionByEventName, ::findInteractionByLabel) - }.unzipFlatten() - - val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } - - // It connects a sensory event to an ingredient places - val sensoryEventArcs: List = sensoryEventWithIngredients.flatMap { et -> - et.event().ingredients.map { ingredient -> - arc(et, createPlace(ingredient.name(), IngredientPlace), 1) - } - } - - val eventThatArePreconditions: List = actionDescriptors.flatMap { actionDescriptor -> - actionDescriptor.requiredEvents + actionDescriptor.requiredOneOfEvents.flatten() - } - - // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net - val sensoryEventArcsNoIngredientsArcs: List = sensoryEventWithoutIngredients - //Filter out events that are preconditions to interactions - .filterNot { sensoryEvent -> eventThatArePreconditions.contains(sensoryEvent.label()) } - .map { sensoryEvent -> - arc(sensoryEvent, createPlace(sensoryEvent.label(), EmptyEventIngredientPlace), 1) - } - - // First find the cases where multiple transitions depend on the same ingredient place - val ingredientsWithMultipleConsumers: Map> = - getIngredientsWithMultipleConsumers(allInteractionTransitions) - - // Add one new transition for each duplicate input (the newly added one in the image above) - val multipleConsumerFacilitatorTransitions: List = - ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) - - val multipleOutputFacilitatorArcs: List = - multipleConsumerFacilitatorTransitions.map { t -> - arc(createPlace(t.label(), IngredientPlace), t, 1) - } - - val interactionArcs: List = - allInteractionTransitions.flatMap { interactionTransition -> - buildInteractionArcs( - multipleConsumerFacilitatorTransitions, - ingredientsWithMultipleConsumers, - interactionEventTransitions, - interactionTransition - ) - } - - val arcs = (interactionArcs + - eventPreconditionArcs + - eventOrPreconditionArcs + - eventLimiterArcs + - sensoryEventArcs + - sensoryEventArcsNoIngredientsArcs + - internalEventArcs + - multipleOutputFacilitatorArcs) - - val petriNet = PetriNet(graph(arcs)) - - val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> - when (val placeType = p.placeType()) { - is FiringLimiterPlace -> p to mapOf(null to placeType.maxLimit()) - else -> null - } - }.toMap() - - val errors = preconditionORErrors + preconditionANDErrors + precompileErrors - - val oldRecipeIdVariant: OldRecipeIdVariant = - when (recipe) { - is JavaRecipe -> Scala212CompatibleJava - is KotlinRecipe -> Scala212CompatibleKotlin - is ScalaRecipe -> Scala212CompatibleScala - else -> throw RecipeValidationException("Only recipe of type java, kotlin or scala supported") - } - - val compiledRecipe = CompiledRecipe.build( - recipe.name(), - petriNet, - initialMarking.mapValues { it.value.mapValues { it.value as Any }.asScala }.asScala, - errors.asScala, - recipe.eventReceivePeriod(), - recipe.retentionPeriod(), - oldRecipeIdVariant, - ) - - return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) - } - - private fun getMultiTransition(internalRepresentationName: String, transitions: List) = - transitions.find { it.label().equals(internalRepresentationName) } - ?: throw NoSuchElementException("No multi transition found with name $internalRepresentationName") - - /** - * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. - * - * @param actionTransitions Seq of reflected transition. - * @return A map from input place name to reflected transitions (where the transitions have as input the place). - */ - private fun getIngredientsWithMultipleConsumers(actionTransitions: List): Map> = - // Obtain a list of field name with their transition - actionTransitions - .flatMap { transition -> - transition.nonProvidedIngredients.map { ingredient -> - ingredient.name() to transition - } - } - .groupBy({ it.first }, { it.second }) - // Only keep those place names which have more than one out-adjacent transition - .filter { (_, interactions) -> interactions.size >= 2 } - - private fun createPlace(label: String, placeType: Place.PlaceType): Place = - Place("${placeType.labelPrepend()}$label", placeType) - - private fun convertCheckpointEventToInteraction(e: CheckPointEvent) = - interaction( - name = "${`package$`.`MODULE$`.checkpointEventInteractionPrefix()}${e.name()}", - inputIngredients = emptyList(), - output = listOf(`Event$`.`MODULE$`.apply(e.name(), emptySequence().asScala)), - requiredEvents = e.requiredEvents, - requiredOneOfEvents = e.requiredOneOfEvents - ) - - private fun convertSieveToInteraction(s: Sieve) = - interaction( - name = "${`package$`.`MODULE$`.sieveInteractionPrefix()}${s.name()}", - inputIngredients = s.inputIngredients, - output = s.output, - requiredEvents = emptySet(), - requiredOneOfEvents = emptySet() - ) - - private fun flattenSubRecipesToInteraction(recipe: Recipe): Set { - fun copyInteraction(i: InteractionDescriptor) = Interaction.apply( - $$"$${`package$`.`MODULE$`.subRecipePrefix()}$${recipe.name()}$$${i.name()}", - i.inputIngredients(), - i.output(), - i.requiredEvents(), - i.requiredOneOfEvents(), - i.predefinedIngredients(), - i.overriddenIngredientNames(), - i.overriddenOutputIngredientName(), - i.maximumInteractionCount(), - i.failureStrategy(), - i.eventOutputTransformers(), - i.isReprovider, - Option.apply(i.originalName()) - ) - return recipe.interactions.map(::copyInteraction).toSet() + - recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + - recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + - recipe.sieves.map(::convertSieveToInteraction) - } - - private fun flattenSensoryEvents(recipe: Recipe): Set = - recipe.sensoryEvents + recipe.subRecipes.flatMap(::flattenSensoryEvents) - - private fun graph(arcs: List): Graph> = - `Graph$`.`MODULE$`.from( - arcs.map { it as WLDiEdge }.asScala, - ClassTag.apply(WLDiEdge::class.java), - CoreConfig(GraphConfig.defaultOrder(), ArraySet.`Hints$`.`MODULE$`.apply(16, 32, 48, 80)) - ) as Graph> - - private fun wlDiEdge(node1: Node, node2: Node, weight: Double, label: L): WLDiEdge = - `WLDiEdge$`.`MODULE$`.newEdge(GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), weight, label) -} - -fun ingredientToCompiledIngredient(ingredient: Ingredient): IngredientDescriptor = - IngredientDescriptor(ingredient.name(), ingredient.ingredientType()) - -fun eventToCompiledEvent(event: Event): EventDescriptor = - EventDescriptor( - event.name(), - event.providedIngredients().map(::ingredientToCompiledIngredient) as Seq - ) - -fun interactionTransitionOf( - interactionDescriptor: InteractionDescriptor, - defaultFailureStrategy: InteractionFailureStrategy, - allIngredientNames: Set -): InteractionTransition { - //This transforms the event using the eventOutputTransformer to the new event - //If there is no eventOutputTransformer for the event the original event is returned - fun transformEventType(event: Event): Event = - when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers[event]) { - null -> event - else -> com.ing.baker.recipe.scaladsl.Event( - eventOutputTransformer.newEventName, - event.providedIngredients.map { i -> - Ingredient( - eventOutputTransformer.ingredientRenames.getOrElse(i.name(), { i.name() }), - i.ingredientType() - ) - }.asScala, - Option.empty() - ) - } - - fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = - com.ing.baker.il.EventOutputTransformer( - recipeEventOutputTransformer.newEventName(), - recipeEventOutputTransformer.ingredientRenames() - ) - - fun transformEventToCompiledEvent(event: Event): EventDescriptor = - EventDescriptor( - event.name(), - event.providedIngredients().map(::ingredientToCompiledIngredient) as Seq - ) - - // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe - // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe - // Replace BakerEventList to BakerEventList tag as know in compiledRecipe - // Replace ingredient tags with overridden tags - val inputFields: Seq> = interactionDescriptor.inputIngredients - .map { ingredient -> - when (ingredient.name()) { - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> ILPackage.recipeInstanceIdName() - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> ILPackage.recipeInstanceMetadataName() - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> ILPackage.recipeInstanceEventListName() - else -> interactionDescriptor.overriddenIngredientNames() - .getOrElse(ingredient.name(), { ingredient.name() }) - } to ingredient.ingredientType() - }.asScala - - val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala - val eventsToFire = - interactionDescriptor.output.map(::transformEventType).map(::transformEventToCompiledEvent).asScala - - //For each ingredient that is not provided - //And is of the type Optional or Option - //Add it to the predefinedIngredients List as empty - //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. - val predefinedIngredientsWithOptionalsEmpty: Map = - inputFields.asJava - .filter { (name, type) -> type is OptionType && name !in allIngredientNames } - .associate { Pair(it.first, `NullValue$`.`MODULE$`) } + - interactionDescriptor.predefinedIngredients - - val p: Triple, Option> = - when (val strategy = interactionDescriptor.failureStrategy().getOrElse { defaultFailureStrategy }) { - is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { - val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { - is Some -> Some( - EventDescriptor( - e.value().getOrElse { null as String? } - ?: (interactionDescriptor.name() + ILPackage.exhaustedEventAppend()), - emptyList().asScala) - ) - - else -> Option.empty() - } - val functionalFailedEvent = when (val e = strategy.fireFunctionalEvent()) { - is Some -> Some( - EventDescriptor( - e.value().getOrElse { null as String? } - ?: (interactionDescriptor.name() + ILPackage.functionalFailedEventAppend()), - emptyList().asScala) - ) - - else -> Option.empty() - } - Triple( - RetryWithIncrementalBackoff( - strategy.initialDelay(), - strategy.backoffFactor(), - strategy.maximumRetries(), - strategy.maxTimeBetweenRetries(), - exhaustedRetryEvent, - functionalFailedEvent, - ), exhaustedRetryEvent, functionalFailedEvent - ) - } - - is InteractionFailureStrategy.BlockInteraction -> Triple( - `BlockInteraction$`.`MODULE$`, - Option.empty(), - Option.empty() - ) - - is InteractionFailureStrategy.FireEventAfterFailure -> { - val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } - val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) - Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) - } - - is InteractionFailureStrategy.FireEventAndBlock -> { - val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } - val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) - Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) - } - - is InteractionFailureStrategy.FireEventAndResolve -> { - val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name() + ILPackage.functionalFailedEventAppend() } - val functionalFailed = EventDescriptor(eventName, emptyList().asScala) - Triple(FireFunctionalEventAfterFailure(functionalFailed), Option.empty(), Some(functionalFailed)) - } - - else -> Triple(`BlockInteraction$`.`MODULE$`, Option.empty(), Option.empty()) - } - val failureStrategy = p.first - val exhaustedRetryEvent = p.second.toList().toSeq() - val functionalRetryEvent = p.third.toList().toSeq() - - // In order to keep compatibility with Scala compiler implementation, we need to use specific Scala collections. - // Otherwise, generated recipe id's hash would be different between the two implementations, which is not desirable. - fun concatEvents(first: Seq, vararg rest: Seq): Seq { - // Handling special case for checkpoint events to keep it compatible with RecipeCompiler Scala implementation. - return if (interactionDescriptor.isSpecialScalaCaseSeq() || first.isEmpty) - scala.collection.immutable.`$colon$colon`.from( - first.toList() - ) - else - rest.fold(first.asJava) { acc, list -> - acc + list.asJava - }.asScala - } - - val eventsToFireAll = concatEvents(eventsToFire, exhaustedRetryEvent, functionalRetryEvent) - val originalEventsAll = concatEvents(originalEvents, exhaustedRetryEvent, functionalRetryEvent) - - // Here the same - keeping recipe id's hash the same as Scala implementation. - fun requiredIngredients(): Seq { - return if (interactionDescriptor.isSpecialScalaCaseSeq() && inputFields.isEmpty()) - scala.collection.immutable.`Nil$`.`MODULE$` as Seq - else if (interactionDescriptor.isSpecialScalaCaseSeq() || (eventsToFireAll.isEmpty() && originalEventsAll.isEmpty())) - scala.collection.immutable.List.from( - inputFields - .asJava - .map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) } - .asScala - ) - .`$plus$plus`(scala.collection.immutable.`Nil$`.`MODULE$` as Seq) as Seq - else - inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala - } - - return InteractionTransition( - eventsToFireAll, - originalEventsAll, - requiredIngredients(), - interactionDescriptor.name(), - interactionDescriptor.originalName(), - predefinedIngredientsWithOptionalsEmpty.asScala, - interactionDescriptor.maximumInteractionCount(), - failureStrategy, - interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> - event.name() to transformEventOutputTransformer( - transformer - ) - }.toMap().asScala, - interactionDescriptor.isReprovider() - ) -} - -private fun InteractionDescriptor.isSpecialScalaCaseSeq() = isCheckpoint() || isSieve() || isSubRecipe() - -private fun InteractionDescriptor.isCheckpoint() = - name().startsWith(`package$`.`MODULE$`.checkpointEventInteractionPrefix()) - -private fun InteractionDescriptor.isSieve() = - name().startsWith("SieveInteraction") || - name().startsWith(`package$`.`MODULE$`.sieveInteractionPrefix()) - -private fun InteractionDescriptor.isSubRecipe() = - name().startsWith(`package$`.`MODULE$`.subRecipePrefix()) - -private fun interaction( - name: String?, - inputIngredients: List, - output: List, - requiredEvents: Set = emptySet(), - requiredOneOfEvents: Set> = emptySet(), - predefinedIngredients: Map = emptyMap(), - overriddenIngredientNames: Map = emptyMap(), - overriddenOutputIngredientName: String? = null, - maximumInteractionCount: Int? = null, - failureStrategy: InteractionFailureStrategy? = null, - eventOutputTransformers: Map = emptyMap(), - isReprovider: Boolean = false, - oldName: String? = null, -) = - Interaction.apply( - name, - inputIngredients.asScala, - output.asScala, - requiredEvents.asScala, - requiredOneOfEvents.map { it: Set -> it.asScala }.toSet().asScala, - predefinedIngredients.asScala, - overriddenIngredientNames.asScala, - Option.apply(overriddenOutputIngredientName), - Option.apply(maximumInteractionCount), - Option.apply(failureStrategy), - eventOutputTransformers.asScala, - isReprovider, - Option.apply(oldName) - ) - -val Recipe.sensoryEvents get() = this.sensoryEvents().asJava -val Recipe.interactions get() = this.interactions().asJava -val Recipe.subRecipes get() = this.subRecipes().asJava -val Recipe.checkpointEvents get() = this.checkpointEvents().asJava -val Recipe.sieves get() = this.sieves().asJava -val InteractionDescriptor.requiredEvents get() = this.requiredEvents().asJava -val InteractionDescriptor.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() -val InteractionDescriptor.output get() = this.output().asJava -val InteractionDescriptor.eventOutputTransformers get() = this.eventOutputTransformers().asJava -val InteractionDescriptor.inputIngredients get() = this.inputIngredients().asJava -val InteractionDescriptor.predefinedIngredients get() = this.predefinedIngredients().asJava -val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava -val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava -val InteractionTransition.maximumInteractionCount - get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } -val Event.providedIngredients get() = this.providedIngredients().asJava -val EventDescriptor.ingredients get() = this.ingredients().asJava -val EventOutputTransformer.ingredientRenames get() = this.ingredientRenames().asJava -val EventOutputTransformer.newEventName: String? get() = this.newEventName() -val CheckPointEvent.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() -val CheckPointEvent.requiredEvents get() = this.requiredEvents().asJava -val Sieve.inputIngredients get() = this.inputIngredients().asJava -val Sieve.output get() = this.output().asJava diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt new file mode 100644 index 000000000..3c90e582f --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt @@ -0,0 +1,8 @@ +package com.ing.baker.recipe + +data class CheckPointEvent( + val name: String = "", + val requiredEvents: Set = emptySet(), + val requiredOneOfEvents: Set> = emptySet(), +) + diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Event.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Event.kt new file mode 100644 index 000000000..fab90cf10 --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Event.kt @@ -0,0 +1,7 @@ +package com.ing.baker.recipe + +data class Event( + val name: String, + val providedIngredients: List, + val maxFiringLimit: Int? = null, +) \ No newline at end of file diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt new file mode 100644 index 000000000..bd89fa51e --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt @@ -0,0 +1,3 @@ +package com.ing.baker.recipe + +data class EventOutputTransformer(val newEventName: String, val ingredientRenames: Map) \ No newline at end of file diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt new file mode 100644 index 000000000..044a350ac --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt @@ -0,0 +1,5 @@ +package com.ing.baker.recipe + +import com.ing.baker.types.Type + +data class Ingredient(val name: String, val type: Type) \ No newline at end of file diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/model/Model.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt similarity index 50% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/model/Model.kt rename to core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt index 64801f784..33155e3ea 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/model/Model.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt @@ -1,50 +1,9 @@ -package com.ing.baker.model +package com.ing.baker.recipe import com.ing.baker.recipe.common.InteractionFailureStrategy -import com.ing.baker.types.Type -import kotlin.collections.mapValues -import kotlin.collections.toMap -import kotlin.time.Duration -import java.lang.reflect.Type as JavaType import com.ing.baker.types.Converters import com.ing.baker.types.Value -data class Event( - val name: String, - val providedIngredients: List, - val maxFiringLimit: Int? = null, -) - -data class CheckPointEvent( - val name: String = "", - val requiredEvents: Set = emptySet(), - val requiredOneOfEvents: Set> = emptySet(), -) - -data class Ingredient(val name: String, val type: Type) - -data class Recipe( - val name: String, - val interactions: List, - val sensoryEvents: Set, - val subRecipes: Set, - val defaultFailureStrategy: InteractionFailureStrategy, - val eventReceivePeriod: Duration? = null, - val retentionPeriod: Duration? = null, - val checkpointEvents: Set, - val sieves: Set, -) - -data class Sieve( - val name: String = "", - val inputIngredients: List, - val output: List, - val function: Any, - val javaTypes: List -) - -data class EventOutputTransformer(val newEventName: String, val ingredientRenames: Map) - data class Interaction( val name: String, val originalName: String, @@ -64,30 +23,23 @@ data class Interaction( @JvmStatic fun of( name: String, - interaction: Interaction, - requiredEvents: Set, - requiredOneOfEvents: Set>, - predefinedIngredients: Map, - overriddenIngredientNames: Map, - eventOutputTransformers: Map, - maximumInteractionCount: Int?, - failureStrategy: InteractionFailureStrategy?, - isReprovider: Boolean, + inputIngredients: List, + output: List ): Interaction = Interaction( name, - interaction.originalName, - interaction.inputIngredients, - interaction.output, - requiredEvents, - requiredOneOfEvents, - predefinedIngredients.mapValues { Converters.toValue(it.value) }, - overriddenIngredientNames, + originalName = name, + inputIngredients, + output, + requiredEvents = emptySet(), + requiredOneOfEvents = emptySet(), + predefinedIngredients = emptyMap(), + overriddenIngredientNames = emptyMap(), overriddenOutputIngredientName = null, - eventOutputTransformers, - maximumInteractionCount, - failureStrategy, - isReprovider + eventOutputTransformers = emptyMap(), + maximumInteractionCount = null, + failureStrategy = null, + isReprovider = false ) @JvmStatic @@ -121,4 +73,4 @@ data class Interaction( isReprovider ) } -} +} \ No newline at end of file diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/model/KotlinBridge.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt similarity index 97% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/model/KotlinBridge.kt rename to core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt index d58bc0b31..b8ac19747 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/model/KotlinBridge.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt @@ -1,18 +1,19 @@ -package com.ing.baker.model +package com.ing.baker.recipe import scala.concurrent.duration.FiniteDuration -import kotlin.collections.mapKeys +import scala.jdk.javaapi.CollectionConverters +import kotlin.time.Duration +import kotlin.time.toKotlinDuration +import com.ing.baker.recipe.common.CheckPointEvent as ScalaCheckPointEvent import com.ing.baker.recipe.common.Event as ScalaEvent -import com.ing.baker.recipe.common.Recipe as ScalaRecipe -import com.ing.baker.recipe.common.Sieve as ScalaSieve -import com.ing.baker.recipe.common.Ingredient as ScalaIngredient import com.ing.baker.recipe.common.EventOutputTransformer as ScalaEventOutputTransformer +import com.ing.baker.recipe.common.Ingredient as ScalaIngredient import com.ing.baker.recipe.common.InteractionDescriptor as ScalaInteraction -import com.ing.baker.recipe.common.CheckPointEvent as ScalaCheckPointEvent -import scala.jdk.javaapi.CollectionConverters -import kotlin.time.Duration +import com.ing.baker.recipe.common.Recipe as ScalaRecipe +import com.ing.baker.recipe.common.Sieve as ScalaSieve import java.time.Duration as JavaDuration -import kotlin.time.toKotlinDuration + +fun Duration.toFiniteDuration(): FiniteDuration = FiniteDuration.fromNanos(inWholeNanoseconds) fun FiniteDuration.toKotlin(): Duration = JavaDuration.ofNanos(toNanos()).toKotlinDuration() diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Recipe.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Recipe.kt new file mode 100644 index 000000000..3f2a37dff --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Recipe.kt @@ -0,0 +1,16 @@ +package com.ing.baker.recipe + +import com.ing.baker.recipe.common.InteractionFailureStrategy +import kotlin.time.Duration + +data class Recipe( + val name: String, + val interactions: List, + val sensoryEvents: Set, + val subRecipes: Set, + val defaultFailureStrategy: InteractionFailureStrategy, + val eventReceivePeriod: Duration? = null, + val retentionPeriod: Duration? = null, + val checkpointEvents: Set, + val sieves: Set, +) \ No newline at end of file diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Sieve.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Sieve.kt new file mode 100644 index 000000000..4848d799c --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Sieve.kt @@ -0,0 +1,11 @@ +package com.ing.baker.recipe + +import java.lang.reflect.Type + +data class Sieve( + val name: String = "", + val inputIngredients: List, + val output: List, + val function: Any, + val javaTypes: List +) \ No newline at end of file diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt index 0b2707e6e..ff8e9aa36 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt @@ -60,7 +60,7 @@ class RecipeCompilerBasicTest { .withSensoryEvents(EventA::class.java) val compiled = RecipeCompiler.compileRecipe(recipe) - Assertions.assertEquals("8682aa9310e38509", compiled.recipeId()) + Assertions.assertEquals("ed72cc8637c9cd07", compiled.recipeId()) } @Test @@ -70,7 +70,7 @@ class RecipeCompilerBasicTest { .withInteraction(InteractionDescriptor.of(InteractionA::class.java)) val compiled = RecipeCompiler.compileRecipe(recipe) - Assertions.assertEquals("796a3cb3eb68b35d", compiled.recipeId()) + Assertions.assertEquals("9b2bc4caf5752697", compiled.recipeId()) } @Test diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt index 3ab8818d0..d8ac80909 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt @@ -34,7 +34,7 @@ class RecipeCompilerDslTest { val compiled = RecipeCompiler.compileRecipe(recipe) - assertEquals("796a3cb3eb68b35d", compiled.recipeId()) + assertEquals("9b2bc4caf5752697", compiled.recipeId()) } @@ -52,7 +52,7 @@ class RecipeCompilerDslTest { val compiled = RecipeCompiler.compileRecipe(recipe) - assertEquals("ec448bcd08163a73", compiled.recipeId()) + assertEquals("4e146307a299c515", compiled.recipeId()) } } \ No newline at end of file diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt index 1c09c626e..d1ab4db7b 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt @@ -61,7 +61,7 @@ import java.util.concurrent.TimeUnit import kotlin.reflect.javaType import kotlin.reflect.typeOf -class RecipeCompilerScalaTest { +class RecipeCompilerFeaturesTest { @Test fun `The recipe compiler should not have validation errors for a valid recipe`() { @@ -722,14 +722,14 @@ class RecipeCompilerScalaTest { val compiledRecipeScala = RecipeCompiler.compileRecipe(recipe) assertEquals(compiledRecipeScala.recipeId(), compiledRecipeKotlin.recipeId()) - assertEquals("220827c42a75b3f8", compiledRecipeKotlin.recipeId()) + assertEquals("9a7f1374762c9b0e", compiledRecipeKotlin.recipeId()) } @Test fun `The recipe compiler should give the interaction with Reprovider enabled when it compiles a java recipe and changes recipeId`() { val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") val compiledRecipe = RecipeCompiler.compileRecipe(recipe) - assertEquals("416e8abc02abcbee", compiledRecipe.recipeId()) + assertEquals("8a9d731ed4dae89e", compiledRecipe.recipeId()) } @Test @@ -743,7 +743,7 @@ class RecipeCompilerScalaTest { ).withRequiredEvent(initialEvent()) ) val compiledRecipe = RecipeCompiler.compileRecipe(recipe) - assertEquals("469441173f91869a", compiledRecipe.recipeId()) + assertEquals("5c6aab1ddb03521f", compiledRecipe.recipeId()) assertEquals(1, compiledRecipe.petriNet().transitions().count { it is InteractionTransition && it.interactionName() .contains("${checkpointEventInteractionPrefix}Success") @@ -805,7 +805,7 @@ class RecipeCompilerScalaTest { } .filter { it.startsWith(subRecipePrefix) }.toSet() - assertEquals("ae2282f55f0a4f9f", compiledRecipe.recipeId()) + assertEquals("5fdfb7538c687289", compiledRecipe.recipeId()) assertEquals( setOf( $$"$SubRecipe$SubSubRecipe$InteractionOne", From faf4c4700acab475438c7751089d509d16858950 Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Thu, 16 Apr 2026 12:41:11 +0200 Subject: [PATCH 06/16] refactoring based on PR feedback --- .../test/java/com/ing/baker/BakerTest.java | 8 +- .../src/test/java/com/ing/baker/Webshop.java | 2 +- .../compiler/JavaCompiledRecipeTest.java | 12 +-- .../com/ing/baker/BakerRuntimeTestBase.scala | 2 +- .../ing/baker/il/RecipeVisualizerSpec.scala | 8 +- .../ing/baker/pbt/RecipePropertiesSpec.scala | 2 +- .../com/ing/baker/runtime/ExamplesSpec.scala | 6 +- .../runtime/akka/BakerExecutionSpec.scala | 6 +- .../baker/runtime/akka/BakerInquireSpec.scala | 4 +- .../baker/runtime/akka/BakerSetupSpec.scala | 16 ++-- .../SensoryEventResponseHandlerSpec.scala | 2 +- .../RecipeManagerActorSpec.scala | 2 +- .../serialization/SerializationSpec.scala | 2 +- .../com/ing/baker/compiler/RecipeCompiler.kt | 2 + .../com/ing/baker/recipe/KotlinBridge.kt | 80 ++++++++++++------- .../baker/compiler/RecipeCompilerBasicTest.kt | 29 ------- .../inmemory/InMemoryMemoryCleanupSpec.scala | 12 +-- .../baker/runtime/model/BakerModelSpec.scala | 2 +- .../model/BakerModelSpecEdgeCasesTests.scala | 2 +- .../model/BakerModelSpecEnquireTests.scala | 4 +- .../model/BakerModelSpecSetupTests.scala | 18 ++--- .../ing/baker/test/recipe/WebshopBaker.scala | 2 +- docs/archive/baker-runtime.md | 6 +- docs/archive/recipe-visualization.md | 4 +- docs/sections/reference/main-abstractions.md | 4 +- docs/sections/reference/runtime.md | 4 +- .../webshop/webservice/WebShopBaker.scala | 2 +- .../java/webshop/JWebshopRecipeTests.java | 8 +- .../webshop/simple/WebshopRecipeSpec.scala | 8 +- .../main/scala/webshop/webservice/Main.scala | 2 +- .../webshop/webservice/WebShopBaker.scala | 2 +- .../examples/java/application/WebShopApp.java | 2 +- .../visualization/WebShopVisualization.java | 2 +- .../scala/application/WebShopApp.scala | 2 +- .../visualization/WebShopVisualization.scala | 2 +- .../java/recipes/WebShopRecipeTest.java | 2 +- .../scala/recipes/WebShopRecipeTest.scala | 2 +- .../http/server/common/RecipeLoaderSpec.scala | 2 +- 38 files changed, 138 insertions(+), 139 deletions(-) diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java index 7655c20ac..2cd737a1f 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java @@ -57,7 +57,7 @@ public static void tearDown() { @Test public void shouldSetupJBakerWithDefaultActorFramework() throws BakerException, ExecutionException, InterruptedException { - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); String recipeInstanceId = UUID.randomUUID().toString(); Baker jBaker = AkkaBaker.java(config, actorSystem, implementationsList); @@ -79,7 +79,7 @@ public void shouldSetupJBakerWithDefaultActorFramework() throws BakerException, @Test public void shouldSetupJBakerWithGivenActorFramework() throws BakerException, ExecutionException, InterruptedException { - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupSimpleRecipe()); assertEquals(compiledRecipe.getValidationErrors().size(), 0); @@ -101,7 +101,7 @@ public void shouldSetupJBakerWithGivenActorFramework() throws BakerException, Ex public void shouldFailWhenMissingImplementations() throws BakerException, ExecutionException, InterruptedException { exception.expect(ExecutionException.class); - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); Baker jBaker = AkkaBaker.java(config, actorSystem); jBaker.addRecipe(RecipeRecord.of(compiledRecipe, System.currentTimeMillis(), true, true)).get(); @@ -118,7 +118,7 @@ public void shouldExecuteCompleteFlow() throws BakerException, ExecutionExceptio jBaker.registerBakerEventListener(bakerEvents::add); // Setup recipe - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JavaCompiledRecipeTest.setupComplexRecipe()); String recipeId = jBaker.addRecipe(RecipeRecord.of(compiledRecipe, System.currentTimeMillis(), false, true)).get(); EventInstance eventOne = EventInstance.from(new JavaCompiledRecipeTest.EventOne()); assertEquals(eventOne.getName(), "EventOne"); diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java index 84a427467..181a494da 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/Webshop.java @@ -138,7 +138,7 @@ public void testWebshop() throws ExecutionException, InterruptedException { Config config = ConfigFactory.load("cassandra.conf"); - CompiledRecipe recipe = RecipeCompiler.INSTANCE.compileRecipe(webshopRecipe); + CompiledRecipe recipe = RecipeCompiler.compileRecipe(webshopRecipe); System.out.println(recipe.getRecipeVisualization()); diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java index 3b0097414..3b0825514 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java @@ -26,7 +26,7 @@ public class JavaCompiledRecipeTest { public void shouldCompileSimpleRecipe() throws BakerException { Recipe recipe = setupSimpleRecipe(); - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); assertEquals(compiledRecipe.getValidationErrors(), new ArrayList()); assertEquals("EventOne", recipe.getEvents().get(0).name()); assertEquals("InteractionOne", recipe.getInteractions().get(0).name()); @@ -37,7 +37,7 @@ public void shouldCompileSimpleRecipe() throws BakerException { @Test public void shouldCompileComplexRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); assertEquals(compiledRecipe.getValidationErrors(), new ArrayList()); assertEquals("EventOne", recipe.getEvents().get(0).name()); assertEquals("EventTwo", recipe.getEvents().get(1).name()); @@ -52,7 +52,7 @@ public void shouldCompileComplexRecipe() throws BakerException { @Test public void shouldShowVisualRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); assertEquals(compiledRecipe.getValidationErrors().size(), 0); String visualRecipe = compiledRecipe.getRecipeVisualization(); Assert.assertTrue("Should contain actionOne", visualRecipe.contains("actionOne")); @@ -67,7 +67,7 @@ public void shouldShowVisualRecipe() throws BakerException { @Test public void shouldShowPetriNetVisual() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compileRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compileRecipe = RecipeCompiler.compileRecipe(recipe); assertEquals(compileRecipe.getValidationErrors().size(), 0); String petrinetVisual = compileRecipe.getPetriNetVisualization(); Assert.assertTrue("Should contain actionOne", petrinetVisual.contains("actionOne")); @@ -80,7 +80,7 @@ public void shouldShowPetriNetVisual() throws BakerException { @Test public void shouldShowFilteredVisualRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compileRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compileRecipe = RecipeCompiler.compileRecipe(recipe); assertEquals(compileRecipe.getValidationErrors().size(), 0); String visualRecipe = compileRecipe.getFilteredRecipeVisualization("InteractionOne"); Assert.assertTrue("Should not contain actionOne", !visualRecipe.contains("InteractionOne")); @@ -94,7 +94,7 @@ public void shouldShowFilteredVisualRecipe() throws BakerException { @Test public void shouldShowFilteredMultipleVisualRecipe() throws BakerException { Recipe recipe = setupComplexRecipe(); - CompiledRecipe compileRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compileRecipe = RecipeCompiler.compileRecipe(recipe); assertEquals(compileRecipe.getValidationErrors().size(), 0); String visualRecipe = compileRecipe.getFilteredRecipeVisualization(new String[]{"actionOne", "EventTwo"}); Assert.assertTrue("Should not contain actionOne", !visualRecipe.contains("actionOne")); diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala index f59885b0c..94950cd2b 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/BakerRuntimeTestBase.scala @@ -217,7 +217,7 @@ trait BakerRuntimeTestBase protected def setupBakerWithRecipe(recipe: Recipe, implementations: List[InteractionInstance]) (implicit actorSystem: ActorSystem): Future[(Baker, String)] = { val baker = AkkaBaker(ConfigFactory.load(), actorSystem, CachingInteractionManager(implementations)) - baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).map(baker -> _)(actorSystem.dispatcher) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).map(baker -> _)(actorSystem.dispatcher) } protected def setupBakerWithNoRecipe()(implicit actorSystem: ActorSystem): Future[Baker] = { diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala index f64485c33..34a01fbf1 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/il/RecipeVisualizerSpec.scala @@ -14,7 +14,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { "be able to visualize a a created compile recipe" in { val recipe: Recipe = getRecipe("VisualizationRecipe") - val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compiledRecipe, RecipeVisualStyle.default) dot should include("interactionOneIngredient -> InteractionThree") @@ -23,7 +23,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { "be able to visualize the created interactions with a filter" in { val recipe: Recipe = getRecipe("filteredVisualRecipe") - val compileRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compileRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compileRecipe, RecipeVisualStyle.default, filter = e => !e.contains("interactionFour")) dot shouldNot contain("interactionFour") } @@ -32,7 +32,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { val recipe: Recipe = Recipe("missingEvent") .withInteraction(interactionOne.withRequiredEvent(secondEvent)) .withSensoryEvent(initialEvent) - val compileRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compileRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compileRecipe, RecipeVisualStyle.default) dot should include("#EE0000") } @@ -41,7 +41,7 @@ class RecipeVisualizerSpec extends AnyWordSpecLike with Matchers { val recipe: Recipe = Recipe("missingEvent") .withInteraction(interactionOne) .withSensoryEvent(secondEvent) - val compileRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compileRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) val dot: String = RecipeVisualizer.visualizeRecipe(compileRecipe, RecipeVisualStyle.default) dot should include("#EE0000") } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala index 675e388bc..0afc032fe 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/pbt/RecipePropertiesSpec.scala @@ -24,7 +24,7 @@ class RecipePropertiesSpec extends AnyFunSuite with Checkers { val prop = forAll(recipeGen) { recipe => val validations = ValidationSettings(allowCycles = false, allowNonExecutableInteractions = false) - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe, validations) + val compiledRecipe = RecipeCompiler.compileRecipe(recipe, validations) if (compiledRecipe.validationErrors.nonEmpty) { logRecipeStats(recipe) diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala index 2e4c9d22a..b6ea7e6cc 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala @@ -24,7 +24,7 @@ class ExamplesSpec extends BakerRuntimeTestBase { "compile without validation errors" in { // compiles the recipe - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(webShopRecipe) + val compiledRecipe = RecipeCompiler.compileRecipe(webShopRecipe) // println(s"Visual recipe: ${compiledRecipe.getRecipeVisualization}") @@ -35,7 +35,7 @@ class ExamplesSpec extends BakerRuntimeTestBase { "run a happy flow" in { // compiles the recipe - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(webShopRecipe) + val compiledRecipe = RecipeCompiler.compileRecipe(webShopRecipe) // test data val testCustomerInfoData = CustomerInfo("John Snow", "Winterfell", "john_snow@hotmail.com") @@ -107,7 +107,7 @@ class ExamplesSpec extends BakerRuntimeTestBase { "compile without validation errors" in { // compiles the recipe - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(openAccountRecipe) + val compiledRecipe = RecipeCompiler.compileRecipe(openAccountRecipe) // prints any validation errors the compiler found Future { compiledRecipe.validationErrors shouldBe empty } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala index f02b59b5e..257833393 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala @@ -389,7 +389,7 @@ class BakerExecutionSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(config, ActorSystem.apply("remoteTest", config), CachingInteractionManager(mockImplementations)) for { - recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) _ = when(testInteractionOneMock.apply(anyString(), anyString())).thenReturn(Future.successful(InteractionOneSuccessful(interactionOneIngredientValue))) recipeInstanceId = UUID.randomUUID().toString _ <- baker.bake(recipeId, recipeInstanceId) @@ -718,7 +718,7 @@ class BakerExecutionSpec extends BakerRuntimeTestBase { .withSensoryEvent(initialEvent) val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(mockImplementations)) - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) for { recipeId <- baker.addRecipe(RecipeRecord.of(compiledRecipe)) @@ -1360,7 +1360,7 @@ class BakerExecutionSpec extends BakerRuntimeTestBase { val recoveryRecipeName = "RecoveryRecipe" val recipeInstanceId = UUID.randomUUID().toString - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(getRecipe(recoveryRecipeName)) + val compiledRecipe = RecipeCompiler.compileRecipe(getRecipe(recoveryRecipeName)) val first = (for { baker1 <- setupBakerWithNoRecipe()(system1) diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala index 913874b59..8cfd28130 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerInquireSpec.scala @@ -35,7 +35,7 @@ class BakerInquireSpec extends BakerRuntimeTestBase { "return all recipes if asked" in { for { (baker, recipeId) <- setupBakerWithRecipe("returnAllRecipes", false) - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnAllRecipes2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnAllRecipes2")))) recipes <- baker.getAllRecipes _ = recipes.size shouldBe 2 _ = recipes(recipeId).compiledRecipe.name shouldBe "returnAllRecipes" @@ -54,7 +54,7 @@ class BakerInquireSpec extends BakerRuntimeTestBase { "return no errors of all recipes if none contain errors if asked" in { for { (baker, recipeId) <- setupBakerWithRecipe("returnHealthAllRecipe", false) - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnHealthAllRecipe2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnHealthAllRecipe2")))) recipeInformations <- baker.getAllRecipes _ = recipeInformations.size shouldBe 2 _ = recipeInformations.get(recipeId) diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala index 6a107be63..b6d7022a7 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala @@ -37,7 +37,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { "bootstrap correctly without throwing an error if provided a correct recipe and correct implementations" when { "correctly load extensions when specified in the configuration" in { - val simpleRecipe = RecipeCompiler.INSTANCE.compileRecipe(Recipe("SimpleRecipe") + val simpleRecipe = RecipeCompiler.compileRecipe(Recipe("SimpleRecipe") .withInteraction(interactionOne) .withSensoryEvent(initialEvent)) @@ -74,7 +74,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new implementations.InteractionOne()))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } @@ -87,7 +87,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new InteractionOneFieldName()))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } @@ -100,7 +100,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } @@ -112,7 +112,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(mock[ComplexIngredientInteraction]))) for { - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } } @@ -127,7 +127,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(mockImplementations)) recoverToExceptionIf[RecipeValidationException] { - baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) }.map(_ should have('message("Recipe NonProvidedIngredient:f31b181c8e36a5c3 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction"))) } @@ -140,7 +140,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager()) recoverToExceptionIf[ImplementationsException] { - baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) }.map(_ should have('message("Recipe MissingImplementation:e1b92d7afa5609d5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)"))) } @@ -153,7 +153,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { val baker = AkkaBaker(ConfigFactory.load(), defaultActorSystem, CachingInteractionManager(InteractionInstance.unsafeFrom(new InteractionOneWrongApply()))) recoverToExceptionIf[ImplementationsException] { - baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) }.map(_ should have('message("Recipe WrongImplementation:7fcdb6b8feb8aa87 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)"))) } } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala index 6f1691560..861b18a38 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala @@ -27,7 +27,7 @@ class SensoryEventResponseHandlerSpec extends AkkaTestBase("SensoryEventResponse import com.ing.baker.recipe.scaladsl._ import Examples.webshop - val webShopRecipe = RecipeCompiler.INSTANCE.compileRecipe(webshop.webShopRecipe) + val webShopRecipe = RecipeCompiler.compileRecipe(webshop.webShopRecipe) "return a SensoryEventResult when processing the outcome of a sensory event with NotifyWhenCompleted reaction" in { val client = TestProbe() diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala index 7de824757..cdec5bfa1 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala @@ -25,7 +25,7 @@ class RecipeManagerActorSpec extends BakerRuntimeTestBase { "The recipe manager" should { "add a recipe to the list when an AddRecipe message is received" in { - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(TestRecipe.getRecipe("AddRecipeRecipe")) + val compiledRecipe = RecipeCompiler.compileRecipe(TestRecipe.getRecipe("AddRecipeRecipe")) val recipeManager: ActorRef = defaultActorSystem.actorOf(RecipeManagerActor.props(), s"recipeManager-${UUID.randomUUID().toString}") for { diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala index 27f522e2c..f6a722372 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala @@ -263,7 +263,7 @@ object SerializationSpec { val eventNameGen: Gen[String] = Gen.alphaStr val finiteDurationGen: Gen[FiniteDuration] = Gen.posNum[Long].map(millis => FiniteDuration(millis, TimeUnit.MILLISECONDS)) - val allTypesRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(AllTypeRecipe.recipe) + val allTypesRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(AllTypeRecipe.recipe) implicit val recipeGen: Gen[CompiledRecipe] = Gen.const(allTypesRecipe) } diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt index 66919dc75..7935bcf4b 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt @@ -90,6 +90,7 @@ object RecipeCompiler { * @param recipe ; The Recipe to compile and execute * @return */ + @JvmStatic fun compileRecipe(recipe: Recipe): CompiledRecipe = compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) @@ -100,6 +101,7 @@ object RecipeCompiler { * @param validationSettings The validation settings to follow for the validation * @return */ + @JvmStatic fun compileRecipe( recipe: Recipe, validationSettings: ValidationSettings diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt index b8ac19747..01c6317f1 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt @@ -1,5 +1,6 @@ package com.ing.baker.recipe +import scala.Option import scala.concurrent.duration.FiniteDuration import scala.jdk.javaapi.CollectionConverters import kotlin.time.Duration @@ -11,42 +12,43 @@ import com.ing.baker.recipe.common.Ingredient as ScalaIngredient import com.ing.baker.recipe.common.InteractionDescriptor as ScalaInteraction import com.ing.baker.recipe.common.Recipe as ScalaRecipe import com.ing.baker.recipe.common.Sieve as ScalaSieve +import scala.collection.Map as ScalaMap +import scala.collection.Seq as ScalaSeq +import scala.collection.Set as ScalaSet import java.time.Duration as JavaDuration -fun Duration.toFiniteDuration(): FiniteDuration = FiniteDuration.fromNanos(inWholeNanoseconds) - fun FiniteDuration.toKotlin(): Duration = JavaDuration.ofNanos(toNanos()).toKotlinDuration() fun ScalaRecipe.toKotlin(): Recipe = Recipe( name = name().orEmpty(), - interactions = CollectionConverters.asJava(interactions()).toList().map { it.toKotlin() }, - sensoryEvents = CollectionConverters.asJava(sensoryEvents()).map { it.toKotlin() }.toSet(), - subRecipes = CollectionConverters.asJava(subRecipes()).map { it.toKotlin() }.toSet(), + interactions = interactions().toKotlinInteractions(), + sensoryEvents = sensoryEvents().toKotlinEvents(), + subRecipes = subRecipes().toKotlinRecipes(), defaultFailureStrategy = defaultFailureStrategy(), - eventReceivePeriod = eventReceivePeriod().map { it.toKotlin() }.getOrElse { null }, - retentionPeriod = retentionPeriod().map { it.toKotlin() }.getOrElse { null }, - checkpointEvents = CollectionConverters.asJava(checkpointEvents()).map { it.toKotlin() }.toSet(), - sieves = CollectionConverters.asJava(this.sieves()).map { it.toKotlin() }.toSet(), + eventReceivePeriod = eventReceivePeriod().toKotlin()?.toKotlin(), + retentionPeriod = retentionPeriod().toKotlin()?.toKotlin(), + checkpointEvents = checkpointEvents().toKotlinCheckPointEvents(), + sieves = sieves().toKotlinSieves(), ) fun ScalaInteraction.toKotlin(): Interaction = Interaction( name = name().orEmpty(), originalName = originalName().orEmpty(), - inputIngredients = CollectionConverters.asJava(inputIngredients()).toList().map { it.toKotlin() }, - output = CollectionConverters.asJava(output()).map { it.toKotlin() }, - requiredEvents = CollectionConverters.asJava(requiredEvents()).toSet(), - requiredOneOfEvents = CollectionConverters.asJava(requiredOneOfEvents()).toSet() - .map { CollectionConverters.asJava(it).toSet() }.toSet(), - predefinedIngredients = CollectionConverters.asJava(predefinedIngredients()).toMap(), - overriddenIngredientNames = CollectionConverters.asJava(overriddenIngredientNames()).toMap(), - overriddenOutputIngredientName = overriddenOutputIngredientName().getOrElse { null }, - eventOutputTransformers = CollectionConverters.asJava(eventOutputTransformers()).toMap() + inputIngredients = inputIngredients().toKotlinIngredients(), + output = output().toKotlinEvents(), + requiredEvents = requiredEvents().toKotlin(), + requiredOneOfEvents = CollectionConverters.asJava(requiredOneOfEvents()).map { it.toKotlin() }.toSet(), + + predefinedIngredients = predefinedIngredients().toKotlin(), + overriddenIngredientNames = overriddenIngredientNames().toKotlin(), + overriddenOutputIngredientName = overriddenOutputIngredientName().toKotlin(), + eventOutputTransformers = eventOutputTransformers().toKotlin() .mapKeys { it.key.toKotlin() } .mapValues { it.value.toKotlin() }, maximumInteractionCount = maximumInteractionCount().getOrElse { null }, - failureStrategy = failureStrategy().getOrElse { null }, + failureStrategy = failureStrategy().toKotlin(), isReprovider = isReprovider() ) @@ -55,26 +57,50 @@ fun ScalaIngredient.toKotlin(): Ingredient = Ingredient(name = name().orEmpty(), fun ScalaSieve.toKotlin(): Sieve = Sieve( name = name().orEmpty(), - inputIngredients = CollectionConverters.asJava(inputIngredients()).toList().map { it.toKotlin() }, - output = CollectionConverters.asJava(output()).map { it.toKotlin() }, + inputIngredients = inputIngredients().toKotlinIngredients(), + output = output().toKotlinEvents(), function = function(), - javaTypes = CollectionConverters.asJava(javaTypes()).toList() + javaTypes = javaTypes().toKotlinTypes() ) fun ScalaCheckPointEvent.toKotlin(): CheckPointEvent = CheckPointEvent( name = name().orEmpty(), - requiredEvents = CollectionConverters.asJava(requiredEvents()).toSet(), - requiredOneOfEvents = CollectionConverters.asJava(requiredOneOfEvents()).toSet() - .map { CollectionConverters.asJava(it).toSet() }.toSet() + requiredEvents = requiredEvents().toKotlin(), + requiredOneOfEvents = CollectionConverters.asJava(requiredOneOfEvents()).map { it.toKotlin() }.toSet(), ) fun ScalaEventOutputTransformer.toKotlin(): EventOutputTransformer = EventOutputTransformer( newEventName = newEventName(), - ingredientRenames = CollectionConverters.asJava(ingredientRenames()).toMap() + ingredientRenames = ingredientRenames().toKotlin() ) fun ScalaEvent.toKotlin(): Event = Event( name = name().orEmpty(), - providedIngredients = CollectionConverters.asJava(providedIngredients()).toList().map { it.toKotlin() }, + providedIngredients = providedIngredients().toKotlinIngredients(), maxFiringLimit = maxFiringLimit().getOrElse { null } ) + +private fun ScalaSeq.toKotlinInteractions(): List = + CollectionConverters.asJava(this).toList().map { it.toKotlin() } + +private fun ScalaSeq.toKotlinEvents(): List = CollectionConverters.asJava(this).toList().map { it.toKotlin() } + +private fun ScalaSet.toKotlinEvents(): Set = CollectionConverters.asJava(this).map { it.toKotlin() }.toSet() + +private fun ScalaSet.toKotlinSieves(): Set = CollectionConverters.asJava(this).map { it.toKotlin() }.toSet() + +private fun ScalaSet.toKotlinRecipes(): Set = CollectionConverters.asJava(this).map { it.toKotlin() }.toSet() + +private fun ScalaSet.toKotlinCheckPointEvents(): Set = + CollectionConverters.asJava(this).map { it.toKotlin() }.toSet() + +private fun ScalaSeq.toKotlinIngredients(): List = + CollectionConverters.asJava(this).toList().map { it.toKotlin() } + +private fun ScalaSet.toKotlin(): Set = CollectionConverters.asJava(this).toSet() + +private fun ScalaSeq.toKotlinTypes(): List = CollectionConverters.asJava(this).toList() + +private fun ScalaMap.toKotlin(): Map = CollectionConverters.asJava(this).toMap() + +private fun Option.toKotlin(): T? = getOrElse { null } diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt index ff8e9aa36..e302e5c60 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt @@ -73,35 +73,6 @@ class RecipeCompilerBasicTest { Assertions.assertEquals("9b2bc4caf5752697", compiled.recipeId()) } - @Test - fun shouldFailOnDuplicatedEventsEvenWithFiringLimitDifference() { - // "Duplicated" EventA - // Because Set backing sensoryEvents has <=4 elements (is a specialized Set.Set4) it only uses `equals` in `contains` - val recipe1 = Recipe("recipe1") - .withSensoryEvents(EventA::class.java, EventB::class.java, EventC::class.java, EventD::class.java) - .withSensoryEventsNoFiringLimit(EventA::class.java) - - // "Duplicated" EventA - // Because Set backing sensoryEvents has >4 elements (is a HashSet) and uses `hashcode` in `contains` - val recipe2 = Recipe("recipe2") - .withSensoryEvents( - EventA::class.java, - EventB::class.java, - EventC::class.java, - EventD::class.java, - EventE::class.java - ) - .withSensoryEventsNoFiringLimit(EventA::class.java) - - Assertions.assertThrows(RecipeValidationException::class.java) { - RecipeCompiler.compileRecipe(recipe1) - } - - Assertions.assertThrows(RecipeValidationException::class.java) { - RecipeCompiler.compileRecipe(recipe2) - } - } - @Test fun shouldAddInteractionsForCheckpointEvents() { val recipe = Recipe("recipe1") diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala index d4086bda8..c2e7985a4 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala @@ -35,7 +35,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { val result: IO[RecipeInstanceState] = for { baker <- InMemoryBaker.build(BakerF.Config(idleTimeout = 100.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List.empty) - recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(TestRecipe.getRecipe("InMemory")), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(TestRecipe.getRecipe("InMemory")), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) result <- baker.getRecipeInstanceState(recipeInstanceId) } yield (result) @@ -65,7 +65,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) @@ -95,7 +95,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(200.milliseconds) @@ -127,7 +127,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) @@ -157,7 +157,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(80.milliseconds) @@ -192,7 +192,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { retentionPeriodCheckInterval = 10.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - recipeId <- baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = false) + recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala index 2fe97d408..d5434a6e7 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala @@ -49,7 +49,7 @@ abstract class BakerModelSpec def setupBakerWithRecipe(recipe: Recipe, implementations: List[InteractionInstance[IO]])(implicit effect: Sync[IO]): IO[(BakerF[IO], String)] = { for { baker <- buildBaker(implementations) - recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe), validate = true)) + recipeId <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe), validate = true)) } yield (baker, recipeId) } diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala index 0c2fa85e8..7b83aff25 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala @@ -32,7 +32,7 @@ trait BakerModelSpecEdgeCasesTests { self: BakerModelSpec => bakerAndRecipeId <- context.setupBakerWithRecipe(recipe, mockImplementations(sync, classTag))(sync) (baker, _) = bakerAndRecipeId - compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + compiledRecipe = RecipeCompiler.compileRecipe(recipe) recipeId <- baker.addRecipe(RecipeRecord.of(compiledRecipe)) recipeInstanceId = UUID.randomUUID().toString diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala index dc823b9bc..7edea189d 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala @@ -23,7 +23,7 @@ trait BakerModelSpecEnquireTests { self: BakerModelSpec => for { bakerWithRecipe <- context.setupBakerWithRecipe("returnAllRecipes", appendUUIDToTheRecipeName = false) (baker, recipeId) = bakerWithRecipe - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnAllRecipes2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnAllRecipes2")))) recipes <- baker.getAllRecipes _ = recipes.size shouldBe 2 _ = recipes(recipeId).compiledRecipe.name shouldBe "returnAllRecipes" @@ -44,7 +44,7 @@ trait BakerModelSpecEnquireTests { self: BakerModelSpec => for { bakerWithRecipe <- context.setupBakerWithRecipe("returnHealthAllRecipe", appendUUIDToTheRecipeName = false) (baker, recipeId) = bakerWithRecipe - recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(getRecipe("returnHealthAllRecipe2")))) + recipeId2 <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(getRecipe("returnHealthAllRecipe2")))) recipeInformations <- baker.getAllRecipes _ = recipeInformations.size shouldBe 2 _ = recipeInformations.get(recipeId) diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala index c691f1b33..e05d04ed4 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala @@ -20,7 +20,7 @@ trait BakerModelSpecSetupTests { def runSetupTests()(implicit sync: Sync[IO], async: Async[IO], classTag: ClassTag[IO[Any]]): Unit = { test("correctly load extensions when specified in the configuration") { context => - val simpleRecipe = RecipeCompiler.INSTANCE.compileRecipe(Recipe("SimpleRecipe") + val simpleRecipe = RecipeCompiler.compileRecipe(Recipe("SimpleRecipe") .withInteraction(interactionOne) .withSensoryEvent(initialEvent)) @@ -52,7 +52,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.setupBakerWithNoRecipe(List(InteractionInstance.unsafeFrom(new InteractionOneSimple()))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } @@ -62,7 +62,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.setupBakerWithNoRecipe(List((InteractionInstance.unsafeFrom(new InteractionOneFieldName())))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } @@ -73,7 +73,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } @@ -83,7 +83,7 @@ trait BakerModelSpecSetupTests { .withSensoryEvent(initialEvent) for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(mock[ComplexIngredientInteraction]))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))) + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) } yield succeed } @@ -95,7 +95,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(mockImplementations) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).attempt.map { + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { case Left(e) => e should have('message("Recipe NonProvidedIngredient:f31b181c8e36a5c3 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction")) case Right(_) => fail("Adding a recipe should fail") } @@ -110,7 +110,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List.empty) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).attempt.map { + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { case Left(e) => e should have('message("Recipe MissingImplementation:e1b92d7afa5609d5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) case Right(_) => fail("Adding a recipe should fail") } @@ -125,7 +125,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(new InteractionOneWrongApply()))) - _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.INSTANCE.compileRecipe(recipe))).attempt.map { + _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { case Left(e) => e should have('message("Recipe WrongImplementation:7fcdb6b8feb8aa87 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) case Right(_) => fail("Adding an interaction should fail") } @@ -142,7 +142,7 @@ trait BakerModelSpecSetupTests { .withRequiredEvents(interactionOneSuccessful)) - val compiled = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compiled = RecipeCompiler.compileRecipe(recipe) for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(testInteractionOneMock))) diff --git a/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala b/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala index e5a48afaf..758404495 100644 --- a/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala +++ b/core/baker-test/src/test/scala/com/ing/baker/test/recipe/WebshopBaker.scala @@ -19,7 +19,7 @@ object WebshopBaker { val javaBaker: javadsl.Baker = new javadsl.Baker(baker) - val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebshopRecipe.recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(WebshopRecipe.recipe) val recipeId: String = Await.result(baker.addRecipe(compiledRecipe, validate = true), 10 seconds) } diff --git a/docs/archive/baker-runtime.md b/docs/archive/baker-runtime.md index a405d543f..aae941116 100644 --- a/docs/archive/baker-runtime.md +++ b/docs/archive/baker-runtime.md @@ -78,13 +78,13 @@ For this purpose there is the `RecipeCompiler`. === "Scala" ```scala - val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) ``` === "Java" ```java - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); ``` !!! hint "Did you know?!" @@ -117,7 +117,7 @@ ValidateOrderImpl validateOrderImpl = new ValidateOrderImpl(); ManufactureGoodsImpl manufactureGoodsImpl = new ManufactureGoodsImpl(); // Compiling the Recipe -CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); +CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); // Creating a Baker Runtime JBaker baker = new JBaker(); diff --git a/docs/archive/recipe-visualization.md b/docs/archive/recipe-visualization.md index 21284064a..22448c919 100644 --- a/docs/archive/recipe-visualization.md +++ b/docs/archive/recipe-visualization.md @@ -9,7 +9,7 @@ Baker can turn a recipe into a .dot representation. `.dot` is a notation for rep === "Scala" ``` scala - val recipe = RecipeCompiler.INSTANCE.compileRecipe(Examples.webshop.webShopRecipe) + val recipe = RecipeCompiler.compileRecipe(Examples.webshop.webShopRecipe) println(recipe.getRecipeVisualization) ``` @@ -17,7 +17,7 @@ Baker can turn a recipe into a .dot representation. `.dot` is a notation for rep === "Java" ```java - final CompiledRecipe recipe = RecipeCompiler.INSTANCE.compileRecipe(Examples.webshop.webShopRecipe); + final CompiledRecipe recipe = RecipeCompiler.compileRecipe(Examples.webshop.webShopRecipe); System.out.println(recipe.getRecipeVisualization()); ``` diff --git a/docs/sections/reference/main-abstractions.md b/docs/sections/reference/main-abstractions.md index 844de0f86..30b0c981c 100644 --- a/docs/sections/reference/main-abstractions.md +++ b/docs/sections/reference/main-abstractions.md @@ -524,7 +524,7 @@ the `Interaction` ActorSystem("WebshopSystem") val baker: Baker = AkkaBaker.localDefault(actorSystem) - val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebshopRecipe.recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(WebshopRecipe.recipe) val program: Future[Unit] = for { _ <- baker.addInteractionInstance(WebshopInstances.ReserveItemsInstance) @@ -567,7 +567,7 @@ the `Interaction` EventInstance.from(new JWebshopRecipe.OrderPlaced("order-uuid", items)); InteractionInstance reserveItemsInstance = InteractionInstance.from(new ReserveItems()); - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); String recipeInstanceId = "first-instance-id"; CompletableFuture> result = baker.addInteractionInstance(reserveItemsInstance) diff --git a/docs/sections/reference/runtime.md b/docs/sections/reference/runtime.md index 737308169..bb8290135 100644 --- a/docs/sections/reference/runtime.md +++ b/docs/sections/reference/runtime.md @@ -221,7 +221,7 @@ into a [visualization](../../cookbook/visualizations). import com.ing.baker.compiler.RecipeCompiler import com.ing.baker.il.CompiledRecipe - val compiledRecipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) ``` === "Java" @@ -230,7 +230,7 @@ into a [visualization](../../cookbook/visualizations). import com.ing.baker.compiler.RecipeCompiler; import com.ing.baker.il.CompiledRecipe; - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(recipe); ``` ## baker.addRecipe(RecipeRecord.of(compiledRecipe)) diff --git a/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala b/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala index b9bf110f7..21a5d00b3 100644 --- a/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala +++ b/examples/baker-example/src/main/scala/webshop/webservice/WebShopBaker.scala @@ -17,7 +17,7 @@ object WebShopBaker { val logger: Logger = getLogger("webshop.webservice") val checkoutFlowCompiledRecipe: CompiledRecipe = - RecipeCompiler.INSTANCE.compileRecipe(CheckoutFlowRecipe.recipe) + RecipeCompiler.compileRecipe(CheckoutFlowRecipe.recipe) def initRecipes(baker: Baker)(implicit ec: ExecutionContext): IO[String] = { IO.fromFuture(IO(for { diff --git a/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java b/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java index 9bfefd392..da8c8242e 100644 --- a/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java +++ b/examples/baker-example/src/test/java/webshop/JWebshopRecipeTests.java @@ -30,12 +30,12 @@ public class JWebshopRecipeTests { @Test public void shouldCompileTheRecipeWithoutIssues() { - RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); + RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); } @Test public void shouldVisualizeTheRecipeWithoutIssues() { - CompiledRecipe recipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe recipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); String visualization = recipe.getRecipeVisualization(); System.out.println(Console.GREEN() + "Recipe visualization, paste this into webgraphviz.com:"); System.out.println(visualization + Console.RESET()); @@ -44,7 +44,7 @@ public void shouldVisualizeTheRecipeWithoutIssues() { @Test public void shouldRunSimpleInstance() throws ExecutionException, InterruptedException { // Compile the recipe - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); List implementations = ImmutableList.of( new MakePaymentInstance(), @@ -88,7 +88,7 @@ public void shouldRunSimpleInstance() throws ExecutionException, InterruptedExce @Test public void shouldRunSimpleInstanceMockitoSample() throws InterruptedException, TimeoutException, ExecutionException { // Compile the recipe - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(JWebshopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(JWebshopRecipe.recipe); // Setup mock ReserveItems reserveItemsMock = diff --git a/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala b/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala index 84ae0ad22..0c763bf9b 100644 --- a/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala +++ b/examples/baker-example/src/test/scala/webshop/simple/WebshopRecipeSpec.scala @@ -25,17 +25,17 @@ class WebshopRecipeSpec extends TestKit(ActorSystem("baker-webshop-system")) wit } "The WebshopRecipeReflection" should "compile the recipe without errors" in { - RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipeReflection.recipe) + RecipeCompiler.compileRecipe(SimpleWebshopRecipeReflection.recipe) Future.successful(succeed) } "The WebshopRecipe" should "compile the recipe without errors" in { - RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipe.recipe) + RecipeCompiler.compileRecipe(SimpleWebshopRecipe.recipe) Future.successful(succeed) } it should "visualize the recipe" in { - val compiled = RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipe.recipe) + val compiled = RecipeCompiler.compileRecipe(SimpleWebshopRecipe.recipe) val viz: String = compiled.getRecipeVisualization println(Console.GREEN + s"Recipe visualization, paste this into webgraphviz.com:") println(viz + Console.RESET) @@ -71,7 +71,7 @@ class WebshopRecipeSpec extends TestKit(ActorSystem("baker-webshop-system")) wit InteractionInstance.unsafeFrom(new ReserveItemsMock) val baker: Baker = AkkaBaker.localDefault(system, CachingInteractionManager(reserveItemsInstance)) - val compiled = RecipeCompiler.INSTANCE.compileRecipe(SimpleWebshopRecipe.recipe) + val compiled = RecipeCompiler.compileRecipe(SimpleWebshopRecipe.recipe) val recipeInstanceId: String = UUID.randomUUID().toString val orderId: String = "order-id" diff --git a/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala b/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala index 8467ba478..e7cc64144 100644 --- a/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala +++ b/examples/bakery-client-example/src/main/scala/webshop/webservice/Main.scala @@ -14,7 +14,7 @@ object Main extends IOApp { override def run(args: List[String]): IO[ExitCode] = { - val compiled = RecipeCompiler.INSTANCE.compileRecipe(CheckoutFlowRecipe.recipe) + val compiled = RecipeCompiler.compileRecipe(CheckoutFlowRecipe.recipe) val checkoutRecipeId = compiled.recipeId /* val protoRecipe: Array[Byte] = ProtoMap.ctxToProto(compiled).toByteArray diff --git a/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala b/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala index 7a2ad999b..78076e30f 100644 --- a/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala +++ b/examples/bakery-client-example/src/main/scala/webshop/webservice/WebShopBaker.scala @@ -12,7 +12,7 @@ import java.util.UUID object WebShopBaker { val checkoutFlowCompiledRecipe: CompiledRecipe = - RecipeCompiler.INSTANCE.compileRecipe(CheckoutFlowRecipe.recipe) + RecipeCompiler.compileRecipe(CheckoutFlowRecipe.recipe) } class WebShopBaker(baker: Baker, checkoutRecipeId: String) extends WebShop with LazyLogging { diff --git a/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java b/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java index e1a5a8ece..0c5b00013 100644 --- a/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java +++ b/examples/docs-code-snippets/src/main/java/examples/java/application/WebShopApp.java @@ -25,7 +25,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc var recipeInstanceId = UUID.randomUUID().toString(); var sensoryEvent = EventInstance.from(createOrderPlaced()); - var recipeId = baker.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe), true).get(); + var recipeId = baker.addRecipe(RecipeCompiler.compileRecipe(WebShopRecipe.recipe), true).get(); baker.bake(recipeId, recipeInstanceId).get(); baker.fireSensoryEventAndAwaitReceived(recipeInstanceId, sensoryEvent).get(); baker.awaitCompleted(recipeInstanceId, Duration.ofSeconds(5)).get(); diff --git a/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java b/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java index e98015552..57b211029 100644 --- a/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java +++ b/examples/docs-code-snippets/src/main/java/examples/java/visualization/WebShopVisualization.java @@ -6,7 +6,7 @@ public class WebShopVisualization { public void printVisualizationString() { - CompiledRecipe compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe); + CompiledRecipe compiledRecipe = RecipeCompiler.compileRecipe(WebShopRecipe.recipe); String graphvizString = compiledRecipe.getRecipeVisualization(); System.out.println(graphvizString); } diff --git a/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala b/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala index e0c5e0971..af5af6431 100644 --- a/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala +++ b/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala @@ -30,7 +30,7 @@ class WebShopApp { val sensoryEvent = EventInstance.unsafeFrom(orderPlaced) for { - recipeId <- bakerF.addRecipe(RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe), validate = true) + recipeId <- bakerF.addRecipe(RecipeCompiler.compileRecipe(WebShopRecipe.recipe), validate = true) _ <- bakerF.bake(recipeId, recipeInstanceId) _ <- bakerF.fireSensoryEventAndAwaitReceived(recipeInstanceId, sensoryEvent) _ <- bakerF.awaitCompleted(recipeInstanceId, timeout = 5.seconds) diff --git a/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala b/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala index d318b41f3..e37da969f 100644 --- a/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala +++ b/examples/docs-code-snippets/src/main/scala/examples/scala/visualization/WebShopVisualization.scala @@ -5,7 +5,7 @@ import examples.scala.recipes.WebShopRecipe class WebShopVisualization { def printVisualizationString(): Unit = { - val compiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe) + val compiledRecipe = RecipeCompiler.compileRecipe(WebShopRecipe.recipe) val visualization = compiledRecipe.getRecipeVisualization println(visualization) } diff --git a/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java b/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java index 50c5f6935..660416861 100644 --- a/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java +++ b/examples/docs-code-snippets/src/test/java/examples/java/recipes/WebShopRecipeTest.java @@ -9,7 +9,7 @@ public class WebShopRecipeTest { @Test public void recipeShouldCompileWithoutValidationErrors() { - var validationErrors = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe).getValidationErrors(); + var validationErrors = RecipeCompiler.compileRecipe(WebShopRecipe.recipe).getValidationErrors(); assertTrue( String.format("Recipe compilation resulted in validation errors: \n%s", validationErrors), validationErrors.isEmpty() diff --git a/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala b/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala index 71ce3b6e5..ef1220e99 100644 --- a/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala +++ b/examples/docs-code-snippets/src/test/scala/examples/scala/recipes/WebShopRecipeTest.scala @@ -6,7 +6,7 @@ import org.scalatest.matchers.should.Matchers class WebShopRecipeTest extends AnyFlatSpec with Matchers { "Recipe" should "compile without validation errors" in { - val validationErrors = RecipeCompiler.INSTANCE.compileRecipe(WebShopRecipe.recipe).validationErrors + val validationErrors = RecipeCompiler.compileRecipe(WebShopRecipe.recipe).validationErrors assert(validationErrors.isEmpty) } } diff --git a/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala b/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala index f355aba29..a1374e47a 100644 --- a/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala +++ b/http/baker-http-server/src/test/scala/com/ing/baker/http/server/common/RecipeLoaderSpec.scala @@ -26,7 +26,7 @@ class RecipeLoaderSpec extends AnyFunSuite with Matchers with BeforeAndAfterAll private val gzippedBase64RecipeFile = File.createTempFile("tmp", "recipe-gzipped-base64") override protected def beforeAll(): Unit = { - val recipe: CompiledRecipe = RecipeCompiler.INSTANCE.compileRecipe(Recipe("Webshop") + val recipe: CompiledRecipe = RecipeCompiler.compileRecipe(Recipe("Webshop") .withSensoryEvents( Event[OrderPlaced], Event[PaymentMade]) From 09d25ebc4f91e10ef9d3323261b4075b0b7db997 Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Thu, 16 Apr 2026 15:02:30 +0200 Subject: [PATCH 07/16] reverting previous test with some refactoring (scala vs kotlin case) --- .../baker/compiler/RecipeCompilerBasicTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt index e302e5c60..cc84781b5 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt @@ -73,6 +73,22 @@ class RecipeCompilerBasicTest { Assertions.assertEquals("9b2bc4caf5752697", compiled.recipeId()) } + @Test + fun shouldFailOnDuplicatedEventsEvenWithFiringLimitDifference() { + // "Duplicated" EventA + val recipe = Recipe("recipe1") + .withSensoryEvents( + EventA::class.java, + EventB::class.java, + EventC::class.java, + EventD::class.java) + .withSensoryEventsNoFiringLimit(EventA::class.java) + + Assertions.assertThrows(RecipeValidationException::class.java) { + RecipeCompiler.compileRecipe(recipe) + } + } + @Test fun shouldAddInteractionsForCheckpointEvents() { val recipe = Recipe("recipe1") From 9427fa79339954415708339e8f2dfa9d0cd26ce4 Mon Sep 17 00:00:00 2001 From: Elmar Wachtmeester Date: Wed, 15 Apr 2026 23:14:16 +0200 Subject: [PATCH 08/16] Add test data builders for RecipeCompiler unit testing Create TestDataBuilders.kt to enable true unit testing by providing helper functions that create Petri net objects without dependencies on the full Recipe DSL pipeline. Key features: - Reuses RecipeCompiler helpers (interactionTransitionOf, createPlace, etc.) - Supports creating EventDescriptor, EventTransition, InteractionTransition - Enables testing individual functions without pipeline setup - Made RecipeComponents and TransitionCollections internal for test access All 82 existing tests still pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../com/ing/baker/compiler/RecipeCompiler.kt | 351 ++++++++-- .../compiler/RecipeCompilerIntegrationTest.kt | 223 +++++++ .../baker/compiler/RecipeCompilerUnitTest.kt | 614 ++++++++++++++++++ .../ing/baker/compiler/TestDataBuilders.kt | 171 +++++ 4 files changed, 1313 insertions(+), 46 deletions(-) create mode 100644 core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt create mode 100644 core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt create mode 100644 core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt index 7935bcf4b..e89dfe2a8 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt @@ -82,6 +82,26 @@ typealias MultiSet = Map */ typealias Marking = Map> +/** + * Holds all components prepared from the recipe during the preparation phase. + */ +internal data class RecipeComponents( + val actionDescriptors: List, + val sensoryEvents: Set, + val allIngredientNames: Set +) + +/** + * Holds all transition types built during the transition building phase. + */ +internal data class TransitionCollections( + val allInteractionTransitions: List, + val sensoryEventTransitions: List, + val interactionEventTransitions: List, + val allEventTransitions: List, + val multipleOutputFacilitatorTransitions: List +) + object RecipeCompiler { /** @@ -109,6 +129,25 @@ object RecipeCompiler { val precompileErrors: List = preCompileAssertions(recipe).asJava + val (actionDescriptors, sensoryEvents, allIngredientNames) = prepareRecipeComponents(recipe) + + val transitions = buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = buildAllPetriNetArcs(actionDescriptors, transitions) + + val preconditionErrors = buildPreconditionErrors(transitions, actionDescriptors) + + return assemblePetriNetAndValidate(recipe, arcs, precompileErrors, preconditionErrors, validationSettings) + } + /** + * Gathers all components from the recipe including action descriptors, sensory events, and ingredient names. + */ + internal fun prepareRecipeComponents(recipe: Recipe): RecipeComponents { // Extend the interactions with the checkpoint event interactions and sub-recipes val actionDescriptors: List = recipe.interactions + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + @@ -134,10 +173,22 @@ object RecipeCompiler { } } + return RecipeComponents(actionDescriptors, sensoryEvents, allIngredientNames) + } + + /** + * Creates interaction transitions, event transitions, and facilitator transitions. + */ + internal fun buildAllTransitions( + actionDescriptors: List, + allIngredientNames: Set, + sensoryEvents: Set, + failureStrategy: InteractionFailureStrategy, + ): TransitionCollections { // For inputs for which no matching output cannot be found, we do not want to generate a place. // It should be provided at runtime from outside the active petri net (marking) val allInteractionTransitions: List = - actionDescriptors.map { interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) } + actionDescriptors.map { interactionTransitionOf(it, failureStrategy, allIngredientNames) } // events provided from outside val sensoryEventTransitions: List = sensoryEvents.map { event -> @@ -156,6 +207,25 @@ object RecipeCompiler { val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions + val multipleOutputFacilitatorTransitions: List = + buildMultipleOutputFacilitatorTransitions(allInteractionTransitions) + + return TransitionCollections( + allInteractionTransitions, + sensoryEventTransitions, + interactionEventTransitions, + allEventTransitions, + multipleOutputFacilitatorTransitions + ) + } + + /** + * Builds internal event arcs that connect interaction transitions to ingredient places. + */ + internal fun buildInternalEventArcs( + allInteractionTransitions: List, + interactionEventTransitions: List + ): List { // Given the event classes, it is creating the ingredient places and // connecting a transition to a ingredient place. val internalEventArcs: List = allInteractionTransitions.flatMap { t -> @@ -168,15 +238,31 @@ object RecipeCompiler { } } } + return internalEventArcs + } - //Create event limiter places so that events can only fire x amount of times. - val eventLimiterArcs: List = sensoryEventTransitions.flatMap { t -> + /** + * Builds event limiter arcs for sensory events with firing limits. + */ + internal fun buildEventLimiterArcs(sensoryEventTransitions: List): List { + val eventLimiterArcs = sensoryEventTransitions.flatMap { t -> when (val n = t.maxFiringLimit().getOrElse { null as Int? }) { null -> emptyList() else -> listOf(arc(createPlace("limit:${t.label()}", FiringLimiterPlace(n)), t)) } } + return eventLimiterArcs + } + + /** + * Builds event precondition arcs (AND and OR) for all interactions. + */ + internal fun buildEventPreconditionArcs( + allEventTransitions: List, + allInteractionTransitions: List, + actionDescriptors: List + ): List { fun findEventTransitionByEventName(eventName: String) = allEventTransitions.find { it.event().name() == eventName } @@ -184,19 +270,28 @@ object RecipeCompiler { allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() // This generates precondition arcs for Required Events (AND). - val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t -> + val eventPreconditionArcs = actionDescriptors.flatMap { t -> buildEventAndPreconditionArcs( t, ::findEventTransitionByEventName, ::findInteractionByLabel ) - }.unzipFlatten() + } // This generates precondition arcs for Required Events (OR). - val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t -> + val eventOrPreconditionArcs = actionDescriptors.flatMap { t -> buildEventORPreconditionArcs(t, ::findEventTransitionByEventName, ::findInteractionByLabel) - }.unzipFlatten() + } + return eventPreconditionArcs + eventOrPreconditionArcs + } + /** + * Builds sensory event arcs connecting sensory events to ingredient places or empty places. + */ + internal fun buildSensoryEventArcs( + sensoryEventTransitions: List, + actionDescriptors: List + ): List { val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } // It connects a sensory event to an ingredient places @@ -217,39 +312,92 @@ object RecipeCompiler { .map { sensoryEvent -> arc(sensoryEvent, createPlace(sensoryEvent.label(), EmptyEventIngredientPlace)) } + return sensoryEventArcs + sensoryEventArcsNoIngredientsArcs + } + + /** + * Builds facilitator transitions for ingredients with multiple consumers. + */ + internal fun buildMultipleOutputFacilitatorTransitions(allInteractionTransitions: List): List { // First find the cases where multiple transitions depend on the same ingredient place val ingredientsWithMultipleConsumers: Map> = getIngredientsWithMultipleConsumers(allInteractionTransitions) // Add one new transition for each duplicate input (the newly added one in the image above) - val multipleConsumerFacilitatorTransitions: List = + val multipleOutputFacilitatorTransitions: List = ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) + return multipleOutputFacilitatorTransitions + } - val multipleOutputFacilitatorArcs: List = - multipleConsumerFacilitatorTransitions.map { t -> - arc(createPlace(t.label(), IngredientPlace), t) - } + /** + * Builds facilitator arcs for ingredients with multiple consumers. + */ + internal fun buildMultipleOutputFacilitatorArcs(multipleOutputFacilitatorTransitions: List): List = + multipleOutputFacilitatorTransitions.map { t -> + arc(createPlace(t.label(), IngredientPlace), t) + } - val interactionArcs: List = - allInteractionTransitions.flatMap { interactionTransition -> - buildInteractionArcs( - multipleConsumerFacilitatorTransitions, - ingredientsWithMultipleConsumers, - interactionEventTransitions, - interactionTransition - ) - } + /** + * Builds interaction arcs for all interactions in the recipe. + */ + internal fun buildInteractionArcs( + allInteractionTransitions: List, + multipleOutputFacilitatorTransitions: List, + interactionEventTransitions: List + ): List { + val ingredientsWithMultipleConsumers = getIngredientsWithMultipleConsumers(allInteractionTransitions) + + return allInteractionTransitions.flatMap { interactionTransition -> + buildInteractionArcs( + multipleOutputFacilitatorTransitions, + ingredientsWithMultipleConsumers, + interactionEventTransitions, + interactionTransition + ) + } + } + + /** + * Consolidates all arc building logic. Arc order is preserved for backward compatibility. + */ + internal fun buildAllPetriNetArcs( + actionDescriptors: List, + transitions: TransitionCollections, + ): List { + val (allInteractionTransitions, sensoryEventTransitions, interactionEventTransitions, allEventTransitions, multipleOutputFacilitatorTransitions) = transitions + + val interactionArcs = buildInteractionArcs(allInteractionTransitions, multipleOutputFacilitatorTransitions, interactionEventTransitions) + + val eventPreconditionArcs = buildEventPreconditionArcs(allEventTransitions, allInteractionTransitions, actionDescriptors) + + val eventLimiterArcs: List = buildEventLimiterArcs(sensoryEventTransitions) + + val sensoryEventArcs = buildSensoryEventArcs(sensoryEventTransitions, actionDescriptors) + + val internalEventArcs: List = buildInternalEventArcs(allInteractionTransitions, interactionEventTransitions) + + val multipleOutputFacilitatorArcs = buildMultipleOutputFacilitatorArcs(multipleOutputFacilitatorTransitions) val arcs = (interactionArcs + eventPreconditionArcs + - eventOrPreconditionArcs + eventLimiterArcs + sensoryEventArcs + - sensoryEventArcsNoIngredientsArcs + internalEventArcs + multipleOutputFacilitatorArcs) + return arcs + } + /** + * Constructs the final Petri net from transitions, arcs, places, and initial marking. + */ + internal fun assemblePetriNetAndValidate( + recipe: Recipe, + arcs: List, + precompileErrors: List, + preconditionErrors: List, + validationSettings: ValidationSettings + ): CompiledRecipe { val petriNet = PetriNet(graph(arcs)) val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> @@ -259,7 +407,7 @@ object RecipeCompiler { } }.toMap() - val errors = preconditionORErrors + preconditionANDErrors + precompileErrors + val errors = preconditionErrors + precompileErrors val compiledRecipe = CompiledRecipe.build( recipe.name(), @@ -274,10 +422,6 @@ object RecipeCompiler { return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) } - fun List, List>>.unzipFlatten() = this.unzip().let { pair -> - pair.first.flatten() to pair.second.flatten() - } - private fun transition(transition: Transition) = Right(transition) private fun place(place: Place) = Left(place) @@ -289,8 +433,84 @@ object RecipeCompiler { /** * Creates a transition for a missing event in the recipe. + * Creates a missing event transition placeholder. + * Used when an interaction requires an event that doesn't exist in the recipe. */ - private fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) + internal fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) + + private fun buildEventAndPreconditionErrors( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = + // Find the event in available events + interaction.requiredEvents.flatMap { eventName -> + buildEventPreconditionErrors( + eventName, + preconditionTransition, + interactionTransition(interaction.name()) + ) + } + + private fun buildEventORPreconditionErrors( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = interaction.requiredOneOfEvents.flatMapIndexed { index: Int, orGroup: Set -> + orGroup.flatMap { eventName -> + buildEventPreconditionErrors( + eventName, + preconditionTransition, + interactionTransition(interaction.name()) + ) + } + } + + private fun buildEventPreconditionErrors( + eventName: String, + preconditionTransition: (String) -> Transition?, + interactionTransition: Transition + ): List { + + val eventTransition = preconditionTransition(eventName) + + val notProvidedError = when (eventTransition) { + null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") + else -> emptyList() + } + + return notProvidedError + } + + /** + * Builds a list of error messages for missing required events. + * Validates that all event preconditions (AND and OR) are satisfied. + * + * @param transitions All transitions in the recipe + * @param actionDescriptors All interaction descriptors in the recipe + * @return List of error messages for missing required events + */ + internal fun buildPreconditionErrors( + transitions: TransitionCollections, + actionDescriptors: List + ): List { + fun findEventTransitionByEventName(eventName: String) = + transitions.allEventTransitions.find { it.event().name() == eventName } + + fun findInteractionByLabel(label: String) = + transitions.allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() + + // This generates precondition errors for Required Events (AND). + val preconditionANDErrors = actionDescriptors.flatMap { t -> + buildEventAndPreconditionErrors(t, ::findEventTransitionByEventName, ::findInteractionByLabel) + } + + // This generates precondition errors for Required Events (OR). + val preconditionORErrors = actionDescriptors.flatMap { t -> + buildEventORPreconditionErrors(t, ::findEventTransitionByEventName, ::findInteractionByLabel) + } + return preconditionORErrors + preconditionANDErrors + } private fun buildEventAndPreconditionArcs( interaction: InteractionDescriptor, @@ -298,7 +518,7 @@ object RecipeCompiler { interactionTransition: (String) -> Transition ) = // Find the event in available events - interaction.requiredEvents.map { eventName -> + interaction.requiredEvents.flatMap { eventName -> // a new `Place` generated for each AND events val eventPreconditionPlace = createPlace( @@ -311,48 +531,43 @@ object RecipeCompiler { preconditionTransition, interactionTransition(interaction.name()) ) - }.unzipFlatten() + } private fun buildEventORPreconditionArcs( interaction: InteractionDescriptor, preconditionTransition: (String) -> Transition?, interactionTransition: (String) -> Transition - ) = interaction.requiredOneOfEvents.mapIndexed { index: Int, orGroup: Set -> + ) = interaction.requiredOneOfEvents.flatMapIndexed { index: Int, orGroup: Set -> // only one `Place` for all the OR events val eventPreconditionPlace = createPlace( label = "${interaction.name()}-or-$index", placeType = EventPreconditionPlace ) - orGroup.map { eventName -> + orGroup.flatMap { eventName -> buildEventPreconditionArcs( eventName, eventPreconditionPlace, preconditionTransition, interactionTransition(interaction.name()) ) - }.unzipFlatten() - }.unzipFlatten() + } + } private fun buildEventPreconditionArcs( eventName: String, preconditionPlace: Place, preconditionTransition: (String) -> Transition?, interactionTransition: Transition - ): Pair, List> { + ): List { val eventTransition = preconditionTransition(eventName) - val notProvidedError = when (eventTransition) { - null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") - else -> emptyList() - } - val arcs = listOf( arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace), arc(preconditionPlace, interactionTransition) ) - return arcs to notProvidedError + return arcs } // the (possible) event output arcs / places @@ -404,7 +619,7 @@ object RecipeCompiler { */ private fun buildInteractionInputArcs( t: InteractionTransition, - multipleConsumerFacilitatorTransitions: List, + multipleOutputFacilitatorTransitions: List, ingredientsWithMultipleConsumers: Map> ): List { @@ -418,7 +633,7 @@ object RecipeCompiler { createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace) listOf( // one arc from multiplier place to the transition - arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), multiTransitionPlace), + arc(getMultiTransition(fieldName, multipleOutputFacilitatorTransitions), multiTransitionPlace), // one arc from extra added place to transition arc(multiTransitionPlace, t) ) @@ -463,6 +678,9 @@ object RecipeCompiler { eventTransitions ) + /** + * Finds a multi-transition (facilitator transition) by its internal representation name. + */ private fun getMultiTransition(internalRepresentationName: String, transitions: List) = transitions.find { it.label().equals(internalRepresentationName) } ?: throw NoSuchElementException("No multi transition found with name $internalRepresentationName") @@ -485,9 +703,19 @@ object RecipeCompiler { // Only keep those place names which have more than one out-adjacent transition .filter { (_, interactions) -> interactions.size >= 2 } - private fun createPlace(label: String, placeType: Place.PlaceType): Place = + /** + * Creates a Place with the given label and type. + * The label is prefixed with the place type's prefix. + * Creates a Place in the Petri net with the appropriate label prefix. + */ + internal fun createPlace(label: String, placeType: Place.PlaceType): Place = Place("${placeType.labelPrepend()}$label", placeType) + /** + * Converts a CheckPointEvent into an InteractionDescriptor. + * Checkpoint events are special events that can be used to track progress in a recipe. + * They are converted to interactions with no input ingredients. + */ private fun convertCheckpointEventToInteraction(e: CheckPointEvent) = interaction( name = "${`package$`.`MODULE$`.checkpointEventInteractionPrefix()}${e.name()}", @@ -497,6 +725,10 @@ object RecipeCompiler { requiredOneOfEvents = e.requiredOneOfEvents ) + /** + * Converts a Sieve into an InteractionDescriptor. + * Sieves are interactions that filter/transform ingredients without requiring specific events. + */ private fun convertSieveToInteraction(s: Sieve) = interaction( name = "${`package$`.`MODULE$`.sieveInteractionPrefix()}${s.name()}", @@ -504,6 +736,10 @@ object RecipeCompiler { output = s.output, ) + /** + * Flattens sub-recipes into InteractionDescriptors. + * Each interaction in a sub-recipe is copied and prefixed to avoid name conflicts. + */ private fun flattenSubRecipesToInteraction(recipe: Recipe): Set { fun copyInteraction(i: InteractionDescriptor) = Interaction.apply( $$"$${`package$`.`MODULE$`.subRecipePrefix()}$${recipe.name()}$$${i.name()}", @@ -526,9 +762,16 @@ object RecipeCompiler { recipe.sieves.map(::convertSieveToInteraction) } + /** + * Flattens sensory events from a recipe including events from sub-recipes recursively. + */ private fun flattenSensoryEvents(recipe: Recipe): Set = recipe.sensoryEvents + recipe.subRecipes.flatMap(::flattenSensoryEvents) + /** + * Creates a Petri net graph from a list of arcs. + * Uses the Scala graph library to construct the graph structure. + */ private fun graph(arcs: List): Graph> = `Graph$`.`MODULE$`.from( arcs.map { it as WLDiEdge }.asScala, @@ -536,10 +779,20 @@ object RecipeCompiler { CoreConfig(GraphConfig.defaultOrder(), ArraySet.`Hints$`.`MODULE$`.apply(16, 32, 48, 80)) ) as Graph> + /** + * Creates a weighted labeled directed edge between two nodes. + * Used to create arcs in the Petri net graph. + */ private fun wlDiEdge(node1: Node, node2: Node, label: L): WLDiEdge = `WLDiEdge$`.`MODULE$`.newEdge(GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), 1.0, label) - private fun interactionTransitionOf( + /** + * Converts an InteractionDescriptor to an InteractionTransition for use in the Petri net. + * + * This function transforms recipe-level interaction descriptors into IL-level transitions, + * handling event transformations, ingredient mapping, optional ingredients, and failure strategies. + */ + internal fun interactionTransitionOf( interactionDescriptor: InteractionDescriptor, defaultFailureStrategy: InteractionFailureStrategy, allIngredientNames: Set @@ -691,6 +944,12 @@ object RecipeCompiler { ) } + /** + * Creates an Interaction descriptor with the specified configuration. + * + * Helper function that constructs a Scala Interaction object from Kotlin/Java types, + * converting collections and options to their Scala equivalents. + */ private fun interaction( name: String?, inputIngredients: List, diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt new file mode 100644 index 000000000..6ddedd9a0 --- /dev/null +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt @@ -0,0 +1,223 @@ +package com.ing.baker.compiler + +import com.ing.baker.il.petrinet.* +import com.ing.baker.recipe.annotations.FiresEvent +import com.ing.baker.recipe.annotations.RequiresIngredient +import com.ing.baker.recipe.javadsl.Interaction +import com.ing.baker.recipe.javadsl.InteractionDescriptor +import com.ing.baker.recipe.javadsl.Recipe +import com.ing.baker.types.Converters +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import scala.jdk.javaapi.CollectionConverters +import scala.jdk.javaapi.OptionConverters + +/** + * Integration tests for the refactored RecipeCompiler. + * These tests verify that the full compilation flow works correctly end-to-end. + */ +class RecipeCompilerIntegrationTest { + + // Test events + class OrderPlaced(val orderId: String, val items: List) + class PaymentMade(val paymentId: String) + class OrderConfirmed(val confirmationId: String) + class OrderShipped(val trackingNumber: String) + class ItemsReserved(val reservedItems: List) + class PaymentReceived(val amount: Double) + + // Test interactions + interface ReserveItems : Interaction { + @FiresEvent(oneOf = [ItemsReserved::class]) + fun apply( + @RequiresIngredient("orderId") orderId: String, + @RequiresIngredient("items") items: List + ): ItemsReserved + } + + interface ProcessPayment : Interaction { + @FiresEvent(oneOf = [PaymentReceived::class]) + fun apply( + @RequiresIngredient("orderId") orderId: String, + @RequiresIngredient("paymentId") paymentId: String + ): PaymentReceived + } + + interface ShipOrder : Interaction { + @FiresEvent(oneOf = [OrderShipped::class]) + fun apply( + @RequiresIngredient("orderId") orderId: String, + @RequiresIngredient("confirmationId") confirmationId: String + ): OrderShipped + } + + @Test + fun `prepareRecipeComponents should extract all action descriptors`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + // Verify that the recipe compiled successfully + assertNotNull(compiledRecipe) + assertEquals("TestRecipe", compiledRecipe.name()) + + // Verify that interactions were included + val interactionNames = CollectionConverters.asJava(compiledRecipe.petriNet().transitions()) + .filterIsInstance() + .map { it.interactionName() } + + assertTrue(interactionNames.contains("ReserveItems")) + assertTrue(interactionNames.contains("ProcessPayment")) + } + + @Test + fun `buildAllTransitions should create interaction and event transitions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + val petriNet = compiledRecipe.petriNet() + + // Should have sensory event transitions + val eventTransitions = CollectionConverters.asJava(petriNet.transitions()) + .filterIsInstance() + + val sensoryEventNames = eventTransitions + .filter { it.isSensoryEvent() } + .map { it.label() } + + assertTrue(sensoryEventNames.contains("OrderPlaced")) + assertTrue(sensoryEventNames.contains("PaymentMade")) + + // Should have interaction transitions + val interactionTransitions = CollectionConverters.asJava(petriNet.transitions()) + .filterIsInstance() + + assertTrue(interactionTransitions.any { it.interactionName() == "ReserveItems" }) + } + + @Test + fun `buildAllPetriNetArcs should create arcs connecting transitions and places`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + val petriNet = compiledRecipe.petriNet() + + // Verify that arcs were created + val arcCount = petriNet.innerGraph().edges().size() + assertTrue(arcCount > 0, "Should have created arcs") + + // Verify that places were created + val placeCount = petriNet.places().size() + assertTrue(placeCount > 0, "Should have created places") + } + + @Test + fun `assemblePetriNetAndValidate should create a valid compiled recipe`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + // Verify basic properties + assertNotNull(compiledRecipe) + assertEquals("TestRecipe", compiledRecipe.name()) + assertNotNull(compiledRecipe.petriNet()) + assertNotNull(compiledRecipe.recipeId()) + } + + @Test + fun `compiled recipe should handle event preconditions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + .withInteraction( + InteractionDescriptor.of(ReserveItems::class.java) + ) + .withInteraction( + InteractionDescriptor.of(ProcessPayment::class.java) + ) + .withInteraction( + InteractionDescriptor.of(ShipOrder::class.java) + .withRequiredEvents(OrderConfirmed::class.java) + ) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + // Should compile despite missing OrderConfirmed event (validation will catch it) + assertNotNull(compiledRecipe) + + // Should have validation errors for missing event + assertTrue(compiledRecipe.validationErrors().size() > 0) + val errorMessages = CollectionConverters.asJava(compiledRecipe.validationErrors()).joinToString() + assertTrue(errorMessages.contains("OrderConfirmed")) + } + + @Test + fun `compiled recipe should handle multiple interactions consuming same ingredient`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + // Both interactions use orderId - should create facilitator transition + val petriNet = compiledRecipe.petriNet() + val transitionCount = petriNet.transitions().size() + + // Should have: + // - 1 sensory event (OrderPlaced) + // - 2 interaction events (ItemsReserved, PaymentReceived) + // - 2 interactions (ReserveItems, ProcessPayment) + // - 1 facilitator for orderId (consumed by both interactions) + assertTrue(transitionCount >= 6, "Should have facilitator transition for shared ingredient") + } + + @Test + fun `refactored compilation should maintain backward compatibility`() { + // This test ensures that the refactored code produces the same results + // as the original implementation would have + val recipe = Recipe("BackwardCompatTest") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + // Verify structure + assertNotNull(compiledRecipe) + assertEquals("BackwardCompatTest", compiledRecipe.name()) + + // Verify all components present + val petriNet = compiledRecipe.petriNet() + assertTrue(petriNet.transitions().size() >= 4) // At least sensory events + interactions + assertTrue(petriNet.places().size() > 0) + assertTrue(petriNet.innerGraph().edges().size() > 0) + + // Should have no validation errors for this valid recipe + assertEquals(0, compiledRecipe.validationErrors().size()) + } + + @Test + fun `empty recipe should compile without errors`() { + val recipe = Recipe("EmptyRecipe") + + val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + + assertNotNull(compiledRecipe) + assertEquals("EmptyRecipe", compiledRecipe.name()) + assertEquals(0, compiledRecipe.validationErrors().size()) + } +} diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt new file mode 100644 index 000000000..beb8be33a --- /dev/null +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt @@ -0,0 +1,614 @@ +package com.ing.baker.compiler + +import com.ing.baker.compiler.RecipeCompiler.interactionTransitionOf +import com.ing.baker.il.ValidationSettings +import com.ing.baker.recipe.annotations.FiresEvent +import com.ing.baker.recipe.annotations.RequiresIngredient +import com.ing.baker.recipe.javadsl.Interaction +import com.ing.baker.recipe.javadsl.InteractionDescriptor +import com.ing.baker.recipe.javadsl.Recipe +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import scala.jdk.javaapi.CollectionConverters + +/** + * Unit tests for individual refactored methods in RecipeCompiler. + * Each test calls a specific extracted method directly with minimal test data. + */ +class RecipeCompilerUnitTest { + + // Test events + class OrderPlaced(val orderId: String, val items: List) + class PaymentMade(val paymentId: String) + class ItemsReserved(val reservedItems: List) + class PaymentReceived(val amount: Double) + class OrderShipped(val trackingNumber: String) + + // Test interactions + interface ReserveItems : Interaction { + @FiresEvent(oneOf = [ItemsReserved::class]) + fun apply( + @RequiresIngredient("orderId") orderId: String, + @RequiresIngredient("items") items: List + ): ItemsReserved + } + + interface ProcessPayment : Interaction { + @FiresEvent(oneOf = [PaymentReceived::class]) + fun apply( + @RequiresIngredient("orderId") orderId: String, + @RequiresIngredient("paymentId") paymentId: String + ): PaymentReceived + } + + interface ShipOrder : Interaction { + @FiresEvent(oneOf = [OrderShipped::class]) + fun apply( + @RequiresIngredient("orderId") orderId: String, + @RequiresIngredient("reservedItems") items: List + ): OrderShipped + } + + // ========== Phase Function Tests ========== + + @Test + fun `prepareRecipeComponents extracts interactions from recipe`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + + assertEquals(2, actionDescriptors.size, "Should extract 2 interactions") + assertEquals(1, sensoryEvents.count(), "Should extract 1 sensory event") + assertTrue(actionDescriptors.any { it.name() == "ReserveItems" }) + assertTrue(actionDescriptors.any { it.name() == "ProcessPayment" }) + } + + @Test + fun `prepareRecipeComponents extracts all ingredient names`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val (_, _, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + + assertTrue(allIngredientNames.contains("orderId"), "Should include orderId from OrderPlaced") + assertTrue(allIngredientNames.contains("items"), "Should include items from OrderPlaced") + assertTrue(allIngredientNames.contains("reservedItems"), "Should include reservedItems from ItemsReserved output") + } + + @Test + fun `prepareRecipeComponents handles empty recipe`() { + val recipe = Recipe("EmptyRecipe") + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + + assertEquals(0, actionDescriptors.size) + assertEquals(0, sensoryEvents.count()) + assertEquals(0, allIngredientNames.size) + } + + @Test + fun `buildAllTransitions creates interaction transitions for each interaction`() { + val recipe = Recipe("TestRecipe") + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + assertEquals(2, transitions.allInteractionTransitions.size, "Should create 2 interaction transitions") + assertTrue(transitions.allInteractionTransitions.any { it.interactionName() == "ReserveItems" }) + assertTrue(transitions.allInteractionTransitions.any { it.interactionName() == "ProcessPayment" }) + } + + @Test + fun `buildAllTransitions creates sensory event transitions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + assertEquals(2, transitions.sensoryEventTransitions.size, "Should create 2 sensory event transitions") + assertTrue(transitions.sensoryEventTransitions.all { it.isSensoryEvent() }) + assertTrue(transitions.sensoryEventTransitions.any { it.label() == "OrderPlaced" }) + assertTrue(transitions.sensoryEventTransitions.any { it.label() == "PaymentMade" }) + } + + @Test + fun `buildAllTransitions creates interaction event transitions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + assertEquals(1, transitions.interactionEventTransitions.size, "Should create transition for ItemsReserved event") + assertFalse(transitions.interactionEventTransitions[0].isSensoryEvent(), "Interaction events are not sensory") + assertEquals("ItemsReserved", transitions.interactionEventTransitions[0].label()) + } + + @Test + fun `buildAllTransitions creates facilitator transitions for shared ingredients`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + // Both ReserveItems and ProcessPayment require orderId + assertTrue(transitions.multipleOutputFacilitatorTransitions.size > 0, "Should create facilitator for orderId") + assertTrue( + transitions.multipleOutputFacilitatorTransitions.any { it.label() == "orderId" }, + "Should have facilitator transition for orderId" + ) + } + + @Test + fun `buildAllTransitions combines all event transitions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val expectedCount = transitions.sensoryEventTransitions.size + transitions.interactionEventTransitions.size + assertEquals(expectedCount, transitions.allEventTransitions.size, "allEventTransitions should combine both types") + } + + @Test + fun `buildAllPetriNetArcs creates arcs from multiple sources`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + val arcs = RecipeCompiler.buildAllPetriNetArcs(actionDescriptors, transitions) + + assertTrue(arcs.isNotEmpty(), "Should create arcs") + // Arcs should include: interaction arcs, sensory event arcs, internal event arcs, etc. + } + + @Test + fun `assemblePetriNetAndValidate creates CompiledRecipe with Petri net`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + val arcs = RecipeCompiler.buildAllPetriNetArcs(actionDescriptors, transitions) + + val compiledRecipe = RecipeCompiler.assemblePetriNetAndValidate( + recipe, + arcs, + emptyList(), + emptyList(), + ValidationSettings.defaultValidationSettings() + ) + + assertNotNull(compiledRecipe) + assertEquals("TestRecipe", compiledRecipe.name()) + assertNotNull(compiledRecipe.petriNet()) + assertNotNull(compiledRecipe.recipeId()) + } + + @Test + fun `assemblePetriNetAndValidate includes validation errors`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + val arcs = RecipeCompiler.buildAllPetriNetArcs(actionDescriptors, transitions) + + val precompileErrors = listOf("Test error 1") + val preconditionErrors = listOf("Test error 2") + val compiledRecipe = RecipeCompiler.assemblePetriNetAndValidate( + recipe, + arcs, + precompileErrors, + preconditionErrors, + ValidationSettings.defaultValidationSettings() + ) + + val errors = CollectionConverters.asJava(compiledRecipe.validationErrors()) + assertTrue(errors.contains("Test error 1")) + assertTrue(errors.contains("Test error 2")) + } + + // ========== Arc Building Function Tests ========== + + @Test + fun `buildInternalEventArcs creates arcs for event ingredients`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildInternalEventArcs( + transitions.allInteractionTransitions, + transitions.interactionEventTransitions + ) + + // ItemsReserved event has reservedItems ingredient + assertTrue(arcs.isNotEmpty(), "Should create arcs for event ingredients") + } + + @Test + fun `buildEventLimiterArcs creates arcs for sensory event transitions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + + val (_, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + emptyList(), + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildEventLimiterArcs(transitions.sensoryEventTransitions) + + // Sensory events have limiters by default + assertTrue(arcs.isNotEmpty(), "Should create event limiter arcs for sensory events") + } + + @Test + fun `buildEventPreconditionArcs creates arcs for required events (AND)`() { + // Create recipe with interaction that requires a specific event + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction( + InteractionDescriptor.of(ShipOrder::class.java) + .withRequiredEvents(ItemsReserved::class.java, PaymentMade::class.java) + ) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildEventPreconditionArcs( + transitions.allEventTransitions, + transitions.allInteractionTransitions, + actionDescriptors + ) + + // ShipOrder requires ItemsReserved AND PaymentMade + // Should create precondition arcs for both required events + assertTrue(arcs.isNotEmpty(), "Should create arcs for event preconditions") + + // With 2 required events, we expect arcs for both preconditions + // Each precondition creates 2 arcs: event->place and place->interaction + assertTrue(arcs.size >= 4, "Should create arcs for both AND preconditions (2 events * 2 arcs each)") + } + + @Test + fun `buildEventPreconditionArcs creates arcs for required one-of events (OR)`() { + // Create recipe with interaction that requires one of several events + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + .withInteraction( + InteractionDescriptor.of(ShipOrder::class.java) + .withRequiredOneOfEvents(ItemsReserved::class.java, PaymentReceived::class.java) + ) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildEventPreconditionArcs( + transitions.allEventTransitions, + transitions.allInteractionTransitions, + actionDescriptors + ) + + // ShipOrder requires ItemsReserved OR PaymentReceived + // Should create precondition arcs connecting both events to the same OR place + assertTrue(arcs.isNotEmpty(), "Should create arcs for OR event preconditions") + + // With 2 OR events, we expect: 2 arcs from events to shared place + 1 arc from place to interaction + assertTrue(arcs.size >= 3, "Should create arcs for OR preconditions") + } + + @Test + fun `buildEventPreconditionArcs handles interactions without preconditions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildEventPreconditionArcs( + transitions.allEventTransitions, + transitions.allInteractionTransitions, + actionDescriptors + ) + + // No interactions have required events, so no precondition arcs should be created + assertEquals(0, arcs.size, "Should not create arcs when no event preconditions are defined") + } + + @Test + fun `buildPreconditionErrors reports missing required events (AND)`() { + // Create recipe where ShipOrder requires an event that doesn't exist + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction( + InteractionDescriptor.of(ShipOrder::class.java) + .withRequiredEvents(ItemsReserved::class.java, PaymentReceived::class.java) + ) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + + // ShipOrder requires PaymentReceived which doesn't exist (ProcessPayment interaction is missing) + assertTrue(errors.isNotEmpty(), "Should report missing required event") + assertTrue(errors.any { it.contains("PaymentReceived") && it.contains("ShipOrder") }, + "Error should mention missing PaymentReceived event for ShipOrder") + } + + @Test + fun `buildPreconditionErrors reports missing required one-of events (OR)`() { + // Create recipe where interaction requires one of several events, none of which exist + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction( + InteractionDescriptor.of(ShipOrder::class.java) + .withRequiredOneOfEvents(ItemsReserved::class.java, PaymentReceived::class.java) + ) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + + // ShipOrder requires ItemsReserved OR PaymentReceived, neither exists + assertTrue(errors.size >= 2, "Should report all missing OR events") + assertTrue(errors.any { it.contains("ItemsReserved") }, "Should report missing ItemsReserved") + assertTrue(errors.any { it.contains("PaymentReceived") }, "Should report missing PaymentReceived") + } + + @Test + fun `buildPreconditionErrors returns empty when all required events exist`() { + // Create recipe where all required events are provided + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withSensoryEvent(PaymentMade::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + .withInteraction( + InteractionDescriptor.of(ShipOrder::class.java) + .withRequiredEvents(ItemsReserved::class.java, PaymentReceived::class.java) + ) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + + // All required events exist (ItemsReserved from ReserveItems, PaymentReceived from ProcessPayment) + assertEquals(0, errors.size, "Should not report errors when all required events exist") + } + + @Test + fun `buildPreconditionErrors handles interactions without preconditions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + + // No interactions have required events + assertEquals(0, errors.size, "Should not report errors when no preconditions are defined") + } + + @Test + fun `buildSensoryEventArcs creates arcs for events with ingredients`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildSensoryEventArcs(transitions.sensoryEventTransitions, actionDescriptors) + + // OrderPlaced has orderId and items ingredients + assertTrue(arcs.size >= 2, "Should create arcs for each ingredient") + } + + @Test + fun `buildMultipleOutputFacilitatorArcs creates arc for each facilitator transition`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildMultipleOutputFacilitatorArcs(transitions.multipleOutputFacilitatorTransitions) + + // Should have facilitator arcs for shared ingredients (orderId) + assertTrue(arcs.isNotEmpty(), "Should create facilitator arcs") + assertEquals(transitions.multipleOutputFacilitatorTransitions.size, arcs.size, + "Should have one arc per facilitator transition") + } + + @Test + fun `buildInteractionArcs creates arcs for all interactions`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val transitions = RecipeCompiler.buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy() + ) + + val arcs = RecipeCompiler.buildInteractionArcs( + transitions.allInteractionTransitions, + transitions.multipleOutputFacilitatorTransitions, + transitions.interactionEventTransitions + ) + + assertTrue(arcs.isNotEmpty(), "Should create interaction arcs") + // Should have input arcs (ingredients -> interaction) and output arcs (interaction -> events) + } + + // ========== Helper Function Tests ========== + + @Test + fun `buildMultipleOutputFacilitatorTransitions creates transitions for shared ingredients`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val interactionTransitions = actionDescriptors.map { + interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) + } + + val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions(interactionTransitions) + + // Both interactions require orderId + assertTrue(facilitatorTransitions.isNotEmpty(), "Should create facilitator for orderId") + assertTrue(facilitatorTransitions.any { it.label() == "orderId" }) + } + + @Test + fun `buildMultipleOutputFacilitatorTransitions returns empty when no shared ingredients`() { + val recipe = Recipe("TestRecipe") + .withSensoryEvent(OrderPlaced::class.java) + .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val interactionTransitions = actionDescriptors.map { + interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) + } + + val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions(interactionTransitions) + + // Only one interaction, no shared ingredients + assertEquals(0, facilitatorTransitions.size, "Should not create facilitators when no sharing") + } +} \ No newline at end of file diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt new file mode 100644 index 000000000..e72dc0ef5 --- /dev/null +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt @@ -0,0 +1,171 @@ +package com.ing.baker.compiler + +import com.ing.baker.compiler.ScalaConversions.asScala +import com.ing.baker.il.EventDescriptor +import com.ing.baker.il.IngredientDescriptor +import com.ing.baker.il.failurestrategy.`BlockInteraction$` +import com.ing.baker.il.petrinet.EventTransition +import com.ing.baker.il.petrinet.InteractionTransition +import com.ing.baker.recipe.common.Event +import com.ing.baker.recipe.common.Ingredient +import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.types.`Int32$` +import scala.Option + +/** + * Test data builders for creating Petri net objects directly without using the Recipe DSL. + * + * These builders enable true unit testing by allowing tests to create test data in isolation, + * without dependencies on prepareRecipeComponents() or buildAllTransitions(). + * + * The builders reuse RecipeCompiler helper functions where possible to minimize duplication. + */ +object TestDataBuilders { + + /** + * Creates a simple EventDescriptor with the specified name and ingredient names. + * + * @param eventName The name of the event + * @param ingredientNames List of ingredient names (all typed as Int32 for simplicity) + * @return EventDescriptor suitable for testing + */ + fun simpleEventDescriptor( + eventName: String, + ingredientNames: List = emptyList() + ): EventDescriptor { + val ingredients = ingredientNames.map { name -> + IngredientDescriptor(name, `Int32$`.`MODULE$`) + } + return EventDescriptor(eventName, ingredients.asScala) + } + + /** + * Creates an EventTransition for testing. + * + * @param eventName Name of the event + * @param isSensory Whether this is a sensory event (from outside) or interaction event + * @param maxFiringLimit Optional maximum number of times this event can fire + * @param ingredientNames Ingredients provided by this event + * @return EventTransition ready for use in tests + */ + fun eventTransition( + eventName: String, + isSensory: Boolean = true, + maxFiringLimit: Int? = null, + ingredientNames: List = emptyList() + ): EventTransition { + val event = simpleEventDescriptor(eventName, ingredientNames) + val limit: Option = maxFiringLimit?.let { Option.apply(it) } ?: Option.empty() + return EventTransition(event, isSensory, limit) + } + + /** + * Creates a simple InteractionTransition for testing. + * + * Reuses RecipeCompiler.interactionTransitionOf to ensure compatibility. + * + * @param name Name of the interaction + * @param inputIngredientNames List of required ingredient names + * @param outputEventNames List of event names this interaction can fire + * @return InteractionTransition ready for use in tests + */ + fun simpleInteractionTransition( + name: String, + inputIngredientNames: List = emptyList(), + outputEventNames: List = emptyList() + ): InteractionTransition { + // Create minimal ingredients + val ingredients = inputIngredientNames.map { + Ingredient(it, `Int32$`.`MODULE$`) + } + + // Create minimal events + val events = outputEventNames.map { eventName -> + com.ing.baker.recipe.scaladsl.Event( + eventName, + emptyList().asScala, + Option.empty() + ) + } + + // Create minimal InteractionDescriptor using Interaction.apply + val descriptor = com.ing.baker.recipe.scaladsl.Interaction.apply( + name, + ingredients.asScala, + events.asScala, + emptySet().asScala, + emptySet>().asScala, + emptyMap().asScala, + emptyMap().asScala, + Option.empty(), + Option.empty(), + Option.empty(), + emptyMap().asScala, + false, + Option.empty() + ) + + // Reuse RecipeCompiler.interactionTransitionOf + return RecipeCompiler.interactionTransitionOf( + descriptor, + `BlockInteraction$`.`MODULE$` as com.ing.baker.recipe.common.InteractionFailureStrategy, + inputIngredientNames.toSet() + ) + } + + /** + * Creates a TransitionCollections object for testing arc building functions. + * + * @param interactionTransitions List of interaction transitions + * @param sensoryEventTransitions List of sensory event transitions + * @param interactionEventTransitions List of interaction event transitions + * @param facilitatorTransitions List of facilitator transitions (optional) + * @return TransitionCollections ready for use in arc building tests + */ + internal fun transitionCollections( + interactionTransitions: List = emptyList(), + sensoryEventTransitions: List = emptyList(), + interactionEventTransitions: List = emptyList(), + facilitatorTransitions: List = emptyList() + ): TransitionCollections { + return TransitionCollections( + allInteractionTransitions = interactionTransitions, + sensoryEventTransitions = sensoryEventTransitions, + interactionEventTransitions = interactionEventTransitions, + allEventTransitions = sensoryEventTransitions + interactionEventTransitions, + multipleOutputFacilitatorTransitions = facilitatorTransitions + ) + } + + /** + * Creates a minimal InteractionDescriptor for testing precondition validation. + * + * @param name Name of the interaction + * @param requiredEvents Set of event names that must occur before this interaction (AND) + * @param requiredOneOfEvents Set of sets of event names where at least one from each set must occur (OR) + * @return InteractionDescriptor ready for validation tests + */ + fun interactionDescriptorWithPreconditions( + name: String, + requiredEvents: Set = emptySet(), + requiredOneOfEvents: Set> = emptySet() + ): InteractionDescriptor { + val scalaRequiredOneOfEvents = requiredOneOfEvents.map { it.asScala }.toSet().asScala + + return com.ing.baker.recipe.scaladsl.Interaction.apply( + name, + emptyList().asScala, + emptyList().asScala, + requiredEvents.asScala, + scalaRequiredOneOfEvents, + emptyMap().asScala, + emptyMap().asScala, + Option.empty(), + Option.empty(), + Option.empty(), + emptyMap().asScala, + false, + Option.empty() + ) + } +} From d6c0463fd9de492d3a154def350992e6be6b23da Mon Sep 17 00:00:00 2001 From: Elmar Wachtmeester Date: Wed, 15 Apr 2026 23:21:14 +0200 Subject: [PATCH 09/16] Rewrite unit tests to use test data builders for true isolation Converted all arc building and helper function tests to use TestDataBuilders instead of the full Recipe DSL pipeline, achieving true unit testing. Tests rewritten (11 total): - buildInternalEventArcs - now creates transitions/events directly - buildEventLimiterArcs - creates event transitions with firing limits - buildEventPreconditionArcs (3 tests) - uses builder preconditions - buildPreconditionErrors (4 tests) - uses builder transitions - buildSensoryEventArcs - creates event transitions directly - buildMultipleOutputFacilitatorArcs - creates shared ingredient transitions - buildInteractionArcs - creates all components with builders - buildMultipleOutputFacilitatorTransitions (2 tests) - no pipeline needed Enhanced TestDataBuilders: - Added outputEventIngredients parameter to simpleInteractionTransition - Fixed InteractionFailureStrategy to use recipe.common.BlockInteraction() - All builders now properly handle Scala interop Benefits: - Tests run faster (no Recipe DSL overhead) - Tests are truly isolated (no pipeline dependencies) - Clearer test intent (explicit test data creation) - Easier to debug (minimal moving parts) All 82 compiler tests still pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../baker/compiler/RecipeCompilerUnitTest.kt | 399 +++++++++--------- .../ing/baker/compiler/TestDataBuilders.kt | 14 +- 2 files changed, 210 insertions(+), 203 deletions(-) diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt index beb8be33a..ecfebaced 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt @@ -270,71 +270,72 @@ class RecipeCompilerUnitTest { @Test fun `buildInternalEventArcs creates arcs for event ingredients`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create test data using builders + val interactionTransition = TestDataBuilders.simpleInteractionTransition( + name = "ReserveItems", + inputIngredientNames = listOf("orderId", "items"), + outputEventNames = listOf("ItemsReserved"), + outputEventIngredients = mapOf("ItemsReserved" to listOf("reservedItems")) + ) + val eventTransition = TestDataBuilders.eventTransition( + eventName = "ItemsReserved", + isSensory = false, + ingredientNames = listOf("reservedItems") ) val arcs = RecipeCompiler.buildInternalEventArcs( - transitions.allInteractionTransitions, - transitions.interactionEventTransitions + listOf(interactionTransition), + listOf(eventTransition) ) - // ItemsReserved event has reservedItems ingredient + // ItemsReserved event has reservedItems ingredient - should create arcs assertTrue(arcs.isNotEmpty(), "Should create arcs for event ingredients") } @Test fun `buildEventLimiterArcs creates arcs for sensory event transitions`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withSensoryEvent(PaymentMade::class.java) - - val (_, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - emptyList(), - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create sensory event transitions with firing limits using builders + val event1 = TestDataBuilders.eventTransition( + eventName = "OrderPlaced", + isSensory = true, + maxFiringLimit = 1, + ingredientNames = listOf("orderId", "items") + ) + val event2 = TestDataBuilders.eventTransition( + eventName = "PaymentMade", + isSensory = true, + maxFiringLimit = 1, + ingredientNames = listOf("paymentId") ) - val arcs = RecipeCompiler.buildEventLimiterArcs(transitions.sensoryEventTransitions) + val arcs = RecipeCompiler.buildEventLimiterArcs(listOf(event1, event2)) - // Sensory events have limiters by default - assertTrue(arcs.isNotEmpty(), "Should create event limiter arcs for sensory events") + // Sensory events with firing limits should have limiter arcs + assertTrue(arcs.isNotEmpty(), "Should create event limiter arcs for sensory events with firing limits") } @Test fun `buildEventPreconditionArcs creates arcs for required events (AND)`() { - // Create recipe with interaction that requires a specific event - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withSensoryEvent(PaymentMade::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction( - InteractionDescriptor.of(ShipOrder::class.java) - .withRequiredEvents(ItemsReserved::class.java, PaymentMade::class.java) - ) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create event transitions using builders + val itemsReservedEvent = TestDataBuilders.eventTransition("ItemsReserved", isSensory = false) + val paymentMadeEvent = TestDataBuilders.eventTransition("PaymentMade", isSensory = true) + + // Create interaction transition with required events + val shipOrderInteraction = TestDataBuilders.simpleInteractionTransition( + name = "ShipOrder", + inputIngredientNames = listOf("orderId", "reservedItems") + ) + + // Create interaction descriptor with preconditions + val shipOrderDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions( + name = "ShipOrder", + requiredEvents = setOf("ItemsReserved", "PaymentMade") ) val arcs = RecipeCompiler.buildEventPreconditionArcs( - transitions.allEventTransitions, - transitions.allInteractionTransitions, - actionDescriptors + listOf(itemsReservedEvent, paymentMadeEvent), + listOf(shipOrderInteraction), + listOf(shipOrderDescriptor) ) // ShipOrder requires ItemsReserved AND PaymentMade @@ -348,29 +349,26 @@ class RecipeCompilerUnitTest { @Test fun `buildEventPreconditionArcs creates arcs for required one-of events (OR)`() { - // Create recipe with interaction that requires one of several events - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withSensoryEvent(PaymentMade::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - .withInteraction( - InteractionDescriptor.of(ShipOrder::class.java) - .withRequiredOneOfEvents(ItemsReserved::class.java, PaymentReceived::class.java) - ) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create event transitions using builders + val itemsReservedEvent = TestDataBuilders.eventTransition("ItemsReserved", isSensory = false) + val paymentReceivedEvent = TestDataBuilders.eventTransition("PaymentReceived", isSensory = false) + + // Create interaction transition + val shipOrderInteraction = TestDataBuilders.simpleInteractionTransition( + name = "ShipOrder", + inputIngredientNames = listOf("orderId", "reservedItems") + ) + + // Create interaction descriptor with OR preconditions + val shipOrderDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions( + name = "ShipOrder", + requiredOneOfEvents = setOf(setOf("ItemsReserved", "PaymentReceived")) ) val arcs = RecipeCompiler.buildEventPreconditionArcs( - transitions.allEventTransitions, - transitions.allInteractionTransitions, - actionDescriptors + listOf(itemsReservedEvent, paymentReceivedEvent), + listOf(shipOrderInteraction), + listOf(shipOrderDescriptor) ) // ShipOrder requires ItemsReserved OR PaymentReceived @@ -383,23 +381,24 @@ class RecipeCompilerUnitTest { @Test fun `buildEventPreconditionArcs handles interactions without preconditions`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create interaction transitions without preconditions using builders + val reserveItemsInteraction = TestDataBuilders.simpleInteractionTransition( + name = "ReserveItems", + inputIngredientNames = listOf("orderId", "items") + ) + val processPaymentInteraction = TestDataBuilders.simpleInteractionTransition( + name = "ProcessPayment", + inputIngredientNames = listOf("orderId", "paymentId") ) + // Create descriptors without preconditions + val reserveDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ReserveItems") + val processDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ProcessPayment") + val arcs = RecipeCompiler.buildEventPreconditionArcs( - transitions.allEventTransitions, - transitions.allInteractionTransitions, - actionDescriptors + emptyList(), + listOf(reserveItemsInteraction, processPaymentInteraction), + listOf(reserveDescriptor, processDescriptor) ) // No interactions have required events, so no precondition arcs should be created @@ -408,26 +407,25 @@ class RecipeCompilerUnitTest { @Test fun `buildPreconditionErrors reports missing required events (AND)`() { - // Create recipe where ShipOrder requires an event that doesn't exist - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction( - InteractionDescriptor.of(ShipOrder::class.java) - .withRequiredEvents(ItemsReserved::class.java, PaymentReceived::class.java) - ) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create transitions with only one event + val itemsReservedEvent = TestDataBuilders.eventTransition("ItemsReserved", isSensory = false) + val shipOrderTransition = TestDataBuilders.simpleInteractionTransition("ShipOrder") + + val transitions = TestDataBuilders.transitionCollections( + interactionTransitions = listOf(shipOrderTransition), + sensoryEventTransitions = emptyList(), + interactionEventTransitions = listOf(itemsReservedEvent) ) - val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + // Create descriptor that requires both ItemsReserved and PaymentReceived + val shipOrderDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions( + name = "ShipOrder", + requiredEvents = setOf("ItemsReserved", "PaymentReceived") + ) + + val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) - // ShipOrder requires PaymentReceived which doesn't exist (ProcessPayment interaction is missing) + // ShipOrder requires PaymentReceived which doesn't exist assertTrue(errors.isNotEmpty(), "Should report missing required event") assertTrue(errors.any { it.contains("PaymentReceived") && it.contains("ShipOrder") }, "Error should mention missing PaymentReceived event for ShipOrder") @@ -435,23 +433,22 @@ class RecipeCompilerUnitTest { @Test fun `buildPreconditionErrors reports missing required one-of events (OR)`() { - // Create recipe where interaction requires one of several events, none of which exist - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction( - InteractionDescriptor.of(ShipOrder::class.java) - .withRequiredOneOfEvents(ItemsReserved::class.java, PaymentReceived::class.java) - ) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create transitions with no events + val shipOrderTransition = TestDataBuilders.simpleInteractionTransition("ShipOrder") + + val transitions = TestDataBuilders.transitionCollections( + interactionTransitions = listOf(shipOrderTransition), + sensoryEventTransitions = emptyList(), + interactionEventTransitions = emptyList() + ) + + // Create descriptor that requires ItemsReserved OR PaymentReceived (neither exists) + val shipOrderDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions( + name = "ShipOrder", + requiredOneOfEvents = setOf(setOf("ItemsReserved", "PaymentReceived")) ) - val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) // ShipOrder requires ItemsReserved OR PaymentReceived, neither exists assertTrue(errors.size >= 2, "Should report all missing OR events") @@ -461,47 +458,46 @@ class RecipeCompilerUnitTest { @Test fun `buildPreconditionErrors returns empty when all required events exist`() { - // Create recipe where all required events are provided - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withSensoryEvent(PaymentMade::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - .withInteraction( - InteractionDescriptor.of(ShipOrder::class.java) - .withRequiredEvents(ItemsReserved::class.java, PaymentReceived::class.java) - ) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create transitions with all required events + val itemsReservedEvent = TestDataBuilders.eventTransition("ItemsReserved", isSensory = false) + val paymentReceivedEvent = TestDataBuilders.eventTransition("PaymentReceived", isSensory = false) + val shipOrderTransition = TestDataBuilders.simpleInteractionTransition("ShipOrder") + + val transitions = TestDataBuilders.transitionCollections( + interactionTransitions = listOf(shipOrderTransition), + sensoryEventTransitions = emptyList(), + interactionEventTransitions = listOf(itemsReservedEvent, paymentReceivedEvent) ) - val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + // Create descriptor that requires both events (both exist) + val shipOrderDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions( + name = "ShipOrder", + requiredEvents = setOf("ItemsReserved", "PaymentReceived") + ) + + val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) - // All required events exist (ItemsReserved from ReserveItems, PaymentReceived from ProcessPayment) + // All required events exist assertEquals(0, errors.size, "Should not report errors when all required events exist") } @Test fun `buildPreconditionErrors handles interactions without preconditions`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create transitions without preconditions + val reserveItemsTransition = TestDataBuilders.simpleInteractionTransition("ReserveItems") + val processPaymentTransition = TestDataBuilders.simpleInteractionTransition("ProcessPayment") + + val transitions = TestDataBuilders.transitionCollections( + interactionTransitions = listOf(reserveItemsTransition, processPaymentTransition), + sensoryEventTransitions = emptyList(), + interactionEventTransitions = emptyList() ) - val errors = RecipeCompiler.buildPreconditionErrors(transitions, actionDescriptors) + // Create descriptors without preconditions + val reserveDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ReserveItems") + val processDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ProcessPayment") + + val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(reserveDescriptor, processDescriptor)) // No interactions have required events assertEquals(0, errors.size, "Should not report errors when no preconditions are defined") @@ -509,18 +505,14 @@ class RecipeCompilerUnitTest { @Test fun `buildSensoryEventArcs creates arcs for events with ingredients`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create sensory event transition with ingredients using builder + val orderPlacedEvent = TestDataBuilders.eventTransition( + eventName = "OrderPlaced", + isSensory = true, + ingredientNames = listOf("orderId", "items") ) - val arcs = RecipeCompiler.buildSensoryEventArcs(transitions.sensoryEventTransitions, actionDescriptors) + val arcs = RecipeCompiler.buildSensoryEventArcs(listOf(orderPlacedEvent), emptyList()) // OrderPlaced has orderId and items ingredients assertTrue(arcs.size >= 2, "Should create arcs for each ingredient") @@ -528,46 +520,56 @@ class RecipeCompilerUnitTest { @Test fun `buildMultipleOutputFacilitatorArcs creates arc for each facilitator transition`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create interaction transitions that share orderId ingredient + val reserveItemsTransition = TestDataBuilders.simpleInteractionTransition( + name = "ReserveItems", + inputIngredientNames = listOf("orderId", "items") + ) + val processPaymentTransition = TestDataBuilders.simpleInteractionTransition( + name = "ProcessPayment", + inputIngredientNames = listOf("orderId", "paymentId") + ) + + // Build facilitator transitions for shared ingredients + val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + listOf(reserveItemsTransition, processPaymentTransition) ) - val arcs = RecipeCompiler.buildMultipleOutputFacilitatorArcs(transitions.multipleOutputFacilitatorTransitions) + val arcs = RecipeCompiler.buildMultipleOutputFacilitatorArcs(facilitatorTransitions) // Should have facilitator arcs for shared ingredients (orderId) assertTrue(arcs.isNotEmpty(), "Should create facilitator arcs") - assertEquals(transitions.multipleOutputFacilitatorTransitions.size, arcs.size, + assertEquals(facilitatorTransitions.size, arcs.size, "Should have one arc per facilitator transition") } @Test fun `buildInteractionArcs creates arcs for all interactions`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val transitions = RecipeCompiler.buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy() + // Create interaction transitions using builders + val reserveItemsTransition = TestDataBuilders.simpleInteractionTransition( + name = "ReserveItems", + inputIngredientNames = listOf("orderId", "items"), + outputEventNames = listOf("ItemsReserved") + ) + val processPaymentTransition = TestDataBuilders.simpleInteractionTransition( + name = "ProcessPayment", + inputIngredientNames = listOf("orderId", "paymentId"), + outputEventNames = listOf("PaymentReceived") + ) + + // Create event transitions for interaction outputs + val itemsReservedEvent = TestDataBuilders.eventTransition("ItemsReserved", isSensory = false) + val paymentReceivedEvent = TestDataBuilders.eventTransition("PaymentReceived", isSensory = false) + + // Build facilitator transitions + val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + listOf(reserveItemsTransition, processPaymentTransition) ) val arcs = RecipeCompiler.buildInteractionArcs( - transitions.allInteractionTransitions, - transitions.multipleOutputFacilitatorTransitions, - transitions.interactionEventTransitions + listOf(reserveItemsTransition, processPaymentTransition), + facilitatorTransitions, + listOf(itemsReservedEvent, paymentReceivedEvent) ) assertTrue(arcs.isNotEmpty(), "Should create interaction arcs") @@ -578,17 +580,19 @@ class RecipeCompilerUnitTest { @Test fun `buildMultipleOutputFacilitatorTransitions creates transitions for shared ingredients`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val interactionTransitions = actionDescriptors.map { - interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) - } + // Create interaction transitions that share orderId ingredient + val reserveItemsTransition = TestDataBuilders.simpleInteractionTransition( + name = "ReserveItems", + inputIngredientNames = listOf("orderId", "items") + ) + val processPaymentTransition = TestDataBuilders.simpleInteractionTransition( + name = "ProcessPayment", + inputIngredientNames = listOf("orderId", "paymentId") + ) - val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions(interactionTransitions) + val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + listOf(reserveItemsTransition, processPaymentTransition) + ) // Both interactions require orderId assertTrue(facilitatorTransitions.isNotEmpty(), "Should create facilitator for orderId") @@ -597,16 +601,15 @@ class RecipeCompilerUnitTest { @Test fun `buildMultipleOutputFacilitatorTransitions returns empty when no shared ingredients`() { - val recipe = Recipe("TestRecipe") - .withSensoryEvent(OrderPlaced::class.java) - .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) - val interactionTransitions = actionDescriptors.map { - interactionTransitionOf(it, recipe.defaultFailureStrategy(), allIngredientNames) - } + // Create single interaction transition using builder + val reserveItemsTransition = TestDataBuilders.simpleInteractionTransition( + name = "ReserveItems", + inputIngredientNames = listOf("orderId", "items") + ) - val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions(interactionTransitions) + val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + listOf(reserveItemsTransition) + ) // Only one interaction, no shared ingredients assertEquals(0, facilitatorTransitions.size, "Should not create facilitators when no sharing") diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt index e72dc0ef5..7b51f7b9e 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt @@ -3,12 +3,12 @@ package com.ing.baker.compiler import com.ing.baker.compiler.ScalaConversions.asScala import com.ing.baker.il.EventDescriptor import com.ing.baker.il.IngredientDescriptor -import com.ing.baker.il.failurestrategy.`BlockInteraction$` import com.ing.baker.il.petrinet.EventTransition import com.ing.baker.il.petrinet.InteractionTransition import com.ing.baker.recipe.common.Event import com.ing.baker.recipe.common.Ingredient import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.common.InteractionFailureStrategy import com.ing.baker.types.`Int32$` import scala.Option @@ -67,23 +67,27 @@ object TestDataBuilders { * @param name Name of the interaction * @param inputIngredientNames List of required ingredient names * @param outputEventNames List of event names this interaction can fire + * @param outputEventIngredients Map from event name to list of ingredient names for that event * @return InteractionTransition ready for use in tests */ fun simpleInteractionTransition( name: String, inputIngredientNames: List = emptyList(), - outputEventNames: List = emptyList() + outputEventNames: List = emptyList(), + outputEventIngredients: Map> = emptyMap() ): InteractionTransition { // Create minimal ingredients val ingredients = inputIngredientNames.map { Ingredient(it, `Int32$`.`MODULE$`) } - // Create minimal events + // Create minimal events with optional ingredients val events = outputEventNames.map { eventName -> + val eventIngredients = outputEventIngredients[eventName] ?: emptyList() + val ingredientList = eventIngredients.map { Ingredient(it, `Int32$`.`MODULE$`) } com.ing.baker.recipe.scaladsl.Event( eventName, - emptyList().asScala, + ingredientList.asScala, Option.empty() ) } @@ -108,7 +112,7 @@ object TestDataBuilders { // Reuse RecipeCompiler.interactionTransitionOf return RecipeCompiler.interactionTransitionOf( descriptor, - `BlockInteraction$`.`MODULE$` as com.ing.baker.recipe.common.InteractionFailureStrategy, + InteractionFailureStrategy.BlockInteraction(), inputIngredientNames.toSet() ) } From 320967dd5df4b586c68419b88f6739bb41440a82 Mon Sep 17 00:00:00 2001 From: Elmar Wachtmeester Date: Mon, 11 May 2026 13:59:43 +0200 Subject: [PATCH 10/16] =?UTF-8?q?Update=20the=20compiler=20to=20mostly=20u?= =?UTF-8?q?se=20the=20kotlin=20data=20classes.=20Only=20the=20I=E2=80=A6?= =?UTF-8?q?=20(#1940)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update the compiler to mostly use the kotlin data classes. Only the InteractionTransition and it's used clases are scala. Next to all kotlin petrinet * Removed interaction helper functio Made originalName non-nullable in InteractionDescriptor --- .../baker/runtime/akka/BakerSetupSpec.scala | 4 +- .../com/ing/baker/compiler/RecipeCompiler.kt | 211 +++++++----------- .../ing/baker/compiler/ScalaConversions.kt | 18 ++ .../kotlin/com/ing/baker/recipe/Ingredient.kt | 2 +- .../ing/baker/recipe/IngredientDescriptor.kt | 8 + .../com/ing/baker/recipe/Interaction.kt | 85 ++----- .../ing/baker/recipe/InteractionDescriptor.kt | 20 ++ .../com/ing/baker/recipe/KotlinBridge.kt | 2 +- .../baker/compiler/RecipeCompilerBasicTest.kt | 4 +- .../compiler/RecipeCompilerFeaturesTest.kt | 2 +- .../baker/compiler/RecipeCompilerUnitTest.kt | 31 +-- .../ing/baker/compiler/TestDataBuilders.kt | 66 +++--- .../model/BakerModelSpecSetupTests.scala | 4 +- 13 files changed, 206 insertions(+), 251 deletions(-) create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt create mode 100644 core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala index b6d7022a7..bc1c224c9 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala @@ -141,7 +141,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { recoverToExceptionIf[ImplementationsException] { baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe MissingImplementation:e1b92d7afa5609d5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)"))) + }.map(_ should have('message("Recipe MissingImplementation:224cacdbf4b4e350 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)"))) } "a recipe provides an implementation for an interaction and does not comply to the Interaction" in { @@ -154,7 +154,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { recoverToExceptionIf[ImplementationsException] { baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe WrongImplementation:7fcdb6b8feb8aa87 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)"))) + }.map(_ should have('message("Recipe WrongImplementation:165501eaeefc4b2f has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)"))) } } } diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt index e89dfe2a8..be553bfca 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt @@ -12,7 +12,6 @@ import com.ing.baker.il.failurestrategy.`BlockInteraction$` import com.ing.baker.il.failurestrategy.FireEventAfterFailure import com.ing.baker.il.failurestrategy.FireFunctionalEventAfterFailure import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff -import com.ing.baker.il.`package$` import com.ing.baker.il.petrinet.Edge import com.ing.baker.il.petrinet.EventTransition import com.ing.baker.il.petrinet.InteractionTransition @@ -23,16 +22,17 @@ import com.ing.baker.il.petrinet.Place import com.ing.baker.il.petrinet.Place.FiringLimiterPlace import com.ing.baker.il.petrinet.Transition import com.ing.baker.petrinet.api.PetriNet -import com.ing.baker.recipe.common.CheckPointEvent -import com.ing.baker.recipe.common.Event -import com.ing.baker.recipe.common.EventOutputTransformer -import com.ing.baker.recipe.common.Ingredient -import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.CheckPointEvent +import com.ing.baker.recipe.Recipe +import com.ing.baker.recipe.Sieve +import com.ing.baker.recipe.Event +import com.ing.baker.recipe.EventOutputTransformer +import com.ing.baker.recipe.Ingredient +import com.ing.baker.recipe.Interaction +import com.ing.baker.recipe.InteractionDescriptor import com.ing.baker.recipe.common.InteractionFailureStrategy -import com.ing.baker.recipe.common.Recipe -import com.ing.baker.recipe.common.Sieve -import com.ing.baker.recipe.scaladsl.`Event$` -import com.ing.baker.recipe.scaladsl.Interaction +import com.ing.baker.recipe.common.Recipe as ScalaRecipe +import com.ing.baker.recipe.toKotlin import com.ing.baker.types.`NullValue$` import com.ing.baker.types.OptionType import com.ing.baker.types.Type @@ -111,9 +111,23 @@ object RecipeCompiler { * @return */ @JvmStatic - fun compileRecipe(recipe: Recipe): CompiledRecipe = + fun compileRecipe(recipe: ScalaRecipe): CompiledRecipe = compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + @JvmStatic + fun compileRecipe( + recipe: ScalaRecipe, + validationSettings: ValidationSettings + ): CompiledRecipe = + compileRecipe(recipe.toKotlin(), validationSettings) + /** * Compile the given recipe to a technical recipe that is useful for Baker. * @@ -127,7 +141,7 @@ object RecipeCompiler { validationSettings: ValidationSettings ): CompiledRecipe { - val precompileErrors: List = preCompileAssertions(recipe).asJava + val precompileErrors: List = preCompileAssertions(recipe) val (actionDescriptors, sensoryEvents, allIngredientNames) = prepareRecipeComponents(recipe) @@ -135,7 +149,7 @@ object RecipeCompiler { actionDescriptors, allIngredientNames, sensoryEvents, - recipe.defaultFailureStrategy() + recipe.defaultFailureStrategy ) val arcs = buildAllPetriNetArcs(actionDescriptors, transitions) @@ -167,7 +181,7 @@ object RecipeCompiler { when (val transformer = i.eventOutputTransformers[e]) { null -> e.providedIngredients.map(Ingredient::name) else -> e.providedIngredients.map { ingredient -> - transformer.ingredientRenames[ingredient.name()] ?: ingredient.name() + transformer.ingredientRenames[ingredient.name] ?: ingredient.name } } } @@ -194,15 +208,15 @@ object RecipeCompiler { val sensoryEventTransitions: List = sensoryEvents.map { event -> EventTransition( EventDescriptor( - event.name(), - event.providedIngredients().map { IngredientDescriptor(it.name(), it.ingredientType()) } as Seq - ), true, event.maxFiringLimit() + event.name, + event.providedIngredients.map { IngredientDescriptor(it.name, it.type) }.asScala + ), true, event.maxFiringLimit.asScala ) } // events provided by other transitions / actions val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> - t.eventsToFire.map { event -> EventTransition(event, false, Option.empty()) } + t.eventsToFire().asJava.map { event -> EventTransition(event, false, Option.empty()) } } val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions @@ -410,12 +424,12 @@ object RecipeCompiler { val errors = preconditionErrors + precompileErrors val compiledRecipe = CompiledRecipe.build( - recipe.name(), + recipe.name, petriNet, initialMarking.mapValues { it.value.mapValues { it.value as Any }.asScala }.asScala, errors.asScala, - recipe.eventReceivePeriod(), - recipe.retentionPeriod(), + recipe.eventReceivePeriod.asScala, + recipe.retentionPeriod.asScala, Scala212CompatibleScala, ) @@ -448,7 +462,7 @@ object RecipeCompiler { buildEventPreconditionErrors( eventName, preconditionTransition, - interactionTransition(interaction.name()) + interactionTransition(interaction.name) ) } @@ -461,7 +475,7 @@ object RecipeCompiler { buildEventPreconditionErrors( eventName, preconditionTransition, - interactionTransition(interaction.name()) + interactionTransition(interaction.name) ) } } @@ -522,14 +536,14 @@ object RecipeCompiler { // a new `Place` generated for each AND events val eventPreconditionPlace = createPlace( - label = "$eventName-${interaction.name()}", + label = "$eventName-${interaction.name}", placeType = EventPreconditionPlace ) buildEventPreconditionArcs( eventName, eventPreconditionPlace, preconditionTransition, - interactionTransition(interaction.name()) + interactionTransition(interaction.name) ) } @@ -540,7 +554,7 @@ object RecipeCompiler { ) = interaction.requiredOneOfEvents.flatMapIndexed { index: Int, orGroup: Set -> // only one `Place` for all the OR events val eventPreconditionPlace = createPlace( - label = "${interaction.name()}-or-$index", + label = "${interaction.name}-or-$index", placeType = EventPreconditionPlace ) orGroup.flatMap { eventName -> @@ -548,7 +562,7 @@ object RecipeCompiler { eventName, eventPreconditionPlace, preconditionTransition, - interactionTransition(interaction.name()) + interactionTransition(interaction.name) ) } } @@ -717,10 +731,10 @@ object RecipeCompiler { * They are converted to interactions with no input ingredients. */ private fun convertCheckpointEventToInteraction(e: CheckPointEvent) = - interaction( - name = "${`package$`.`MODULE$`.checkpointEventInteractionPrefix()}${e.name()}", + Interaction( + name = "${ILPackage.checkpointEventInteractionPrefix()}${e.name}", inputIngredients = emptyList(), - output = listOf(`Event$`.`MODULE$`.apply(e.name(), emptySequence().asScala)), + output = listOf(Event(e.name, emptyList())), requiredEvents = e.requiredEvents, requiredOneOfEvents = e.requiredOneOfEvents ) @@ -730,10 +744,10 @@ object RecipeCompiler { * Sieves are interactions that filter/transform ingredients without requiring specific events. */ private fun convertSieveToInteraction(s: Sieve) = - interaction( - name = "${`package$`.`MODULE$`.sieveInteractionPrefix()}${s.name()}", + Interaction( + name = "${ILPackage.sieveInteractionPrefix()}${s.name}", inputIngredients = s.inputIngredients, - output = s.output, + output = s.output ) /** @@ -741,20 +755,20 @@ object RecipeCompiler { * Each interaction in a sub-recipe is copied and prefixed to avoid name conflicts. */ private fun flattenSubRecipesToInteraction(recipe: Recipe): Set { - fun copyInteraction(i: InteractionDescriptor) = Interaction.apply( - $$"$${`package$`.`MODULE$`.subRecipePrefix()}$${recipe.name()}$$${i.name()}", - i.inputIngredients(), - i.output(), - i.requiredEvents(), - i.requiredOneOfEvents(), - i.predefinedIngredients(), - i.overriddenIngredientNames(), - i.overriddenOutputIngredientName(), - i.maximumInteractionCount(), - i.failureStrategy(), - i.eventOutputTransformers(), + fun copyInteraction(i: InteractionDescriptor) = com.ing.baker.recipe.Interaction( + $$"$${ILPackage.subRecipePrefix()}$${recipe.name}$$${i.name}", + i.originalName, + i.inputIngredients, + i.output, + i.requiredEvents, + i.requiredOneOfEvents, + i.predefinedIngredients, + i.overriddenIngredientNames, + i.overriddenOutputIngredientName, + i.eventOutputTransformers, + i.maximumInteractionCount, + i.failureStrategy, i.isReprovider, - Option.apply(i.originalName()) ) return recipe.interactions.map(::copyInteraction).toSet() + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + @@ -802,28 +816,28 @@ object RecipeCompiler { fun transformEventType(event: Event): Event = when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers[event]) { null -> event - else -> com.ing.baker.recipe.scaladsl.Event( + else -> Event( eventOutputTransformer.newEventName, event.providedIngredients.map { i -> Ingredient( - eventOutputTransformer.ingredientRenames.getOrElse(i.name(), { i.name() }), - i.ingredientType() + eventOutputTransformer.ingredientRenames.getOrElse(i.name, { i.name }), + i.type ) - }.asScala, - Option.empty() + }, + null ) } fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = com.ing.baker.il.EventOutputTransformer( - recipeEventOutputTransformer.newEventName(), - recipeEventOutputTransformer.ingredientRenames() + recipeEventOutputTransformer.newEventName, + recipeEventOutputTransformer.ingredientRenames.asScala ) fun transformEventToCompiledEvent(event: Event): EventDescriptor = EventDescriptor( - event.name(), - event.providedIngredients().map { IngredientDescriptor(it.name(), it.ingredientType()) } as Seq + event.name, + event.providedIngredients.map { IngredientDescriptor(it.name, it.type) }.asScala ) // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe @@ -832,13 +846,13 @@ object RecipeCompiler { // Replace ingredient tags with overridden tags val inputFields: Seq> = interactionDescriptor.inputIngredients .map { ingredient -> - when (ingredient.name()) { + when (ingredient.name) { com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> ILPackage.recipeInstanceIdName() com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> ILPackage.recipeInstanceMetadataName() com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> ILPackage.recipeInstanceEventListName() - else -> interactionDescriptor.overriddenIngredientNames() - .getOrElse(ingredient.name(), { ingredient.name() }) - } to ingredient.ingredientType() + else -> interactionDescriptor.overriddenIngredientNames + .getOrElse(ingredient.name, { ingredient.name }) + } to ingredient.type }.asScala val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala @@ -856,13 +870,13 @@ object RecipeCompiler { interactionDescriptor.predefinedIngredients val p: Triple, Option> = - when (val strategy = interactionDescriptor.failureStrategy().getOrElse { defaultFailureStrategy }) { + when (val strategy = interactionDescriptor.failureStrategy ?: defaultFailureStrategy) { is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { is Some -> Some( EventDescriptor( e.value().getOrElse { null as String? } - ?: (interactionDescriptor.name() + ILPackage.exhaustedEventAppend()), + ?: (interactionDescriptor.name + ILPackage.exhaustedEventAppend()), emptyList().asScala) ) @@ -872,7 +886,7 @@ object RecipeCompiler { is Some -> Some( EventDescriptor( e.value().getOrElse { null as String? } - ?: (interactionDescriptor.name() + ILPackage.functionalFailedEventAppend()), + ?: (interactionDescriptor.name + ILPackage.functionalFailedEventAppend()), emptyList().asScala) ) @@ -898,21 +912,21 @@ object RecipeCompiler { is InteractionFailureStrategy.FireEventAfterFailure -> { val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } + .getOrElse { interactionDescriptor.name + ILPackage.exhaustedEventAppend() } val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) } is InteractionFailureStrategy.FireEventAndBlock -> { val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name() + ILPackage.exhaustedEventAppend() } + .getOrElse { interactionDescriptor.name + ILPackage.exhaustedEventAppend() } val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) } is InteractionFailureStrategy.FireEventAndResolve -> { val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name() + ILPackage.functionalFailedEventAppend() } + .getOrElse { interactionDescriptor.name + ILPackage.functionalFailedEventAppend() } val functionalFailed = EventDescriptor(eventName, emptyList().asScala) Triple(FireFunctionalEventAfterFailure(functionalFailed), Option.empty(), Some(functionalFailed)) } @@ -930,78 +944,25 @@ object RecipeCompiler { eventsToFireAll.asScala, originalEventsAll.asScala, inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala, - interactionDescriptor.name(), - interactionDescriptor.originalName(), + interactionDescriptor.name, + interactionDescriptor.originalName, predefinedIngredientsWithOptionalsEmpty.asScala, - interactionDescriptor.maximumInteractionCount(), + interactionDescriptor.maximumInteractionCount.asScala, failureStrategy, interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> - event.name() to transformEventOutputTransformer( + event.name to transformEventOutputTransformer( transformer ) }.toMap().asScala, - interactionDescriptor.isReprovider() + interactionDescriptor.isReprovider ) } - - /** - * Creates an Interaction descriptor with the specified configuration. - * - * Helper function that constructs a Scala Interaction object from Kotlin/Java types, - * converting collections and options to their Scala equivalents. - */ - private fun interaction( - name: String?, - inputIngredients: List, - output: List, - requiredEvents: Set = emptySet(), - requiredOneOfEvents: Set> = emptySet(), - predefinedIngredients: Map = emptyMap(), - overriddenIngredientNames: Map = emptyMap(), - overriddenOutputIngredientName: String? = null, - maximumInteractionCount: Int? = null, - failureStrategy: InteractionFailureStrategy? = null, - eventOutputTransformers: Map = emptyMap(), - isReprovider: Boolean = false, - oldName: String? = null, - ) = - Interaction.apply( - name, - inputIngredients.asScala, - output.asScala, - requiredEvents.asScala, - requiredOneOfEvents.map { it: Set -> it.asScala }.toSet().asScala, - predefinedIngredients.asScala, - overriddenIngredientNames.asScala, - Option.apply(overriddenOutputIngredientName), - Option.apply(maximumInteractionCount), - Option.apply(failureStrategy), - eventOutputTransformers.asScala, - isReprovider, - Option.apply(oldName) - ) } -val Recipe.sensoryEvents get() = this.sensoryEvents().asJava -val Recipe.interactions get() = this.interactions().asJava -val Recipe.subRecipes get() = this.subRecipes().asJava -val Recipe.checkpointEvents get() = this.checkpointEvents().asJava -val Recipe.sieves get() = this.sieves().asJava -val InteractionDescriptor.requiredEvents get() = this.requiredEvents().asJava -val InteractionDescriptor.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() -val InteractionDescriptor.output get() = this.output().asJava -val InteractionDescriptor.eventOutputTransformers get() = this.eventOutputTransformers().asJava -val InteractionDescriptor.inputIngredients get() = this.inputIngredients().asJava -val InteractionDescriptor.predefinedIngredients get() = this.predefinedIngredients().asJava val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava val InteractionTransition.maximumInteractionCount get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } -val Event.providedIngredients get() = this.providedIngredients().asJava val EventDescriptor.ingredients get() = this.ingredients().asJava -val EventOutputTransformer.ingredientRenames get() = this.ingredientRenames().asJava -val EventOutputTransformer.newEventName: String? get() = this.newEventName() -val CheckPointEvent.requiredOneOfEvents get() = this.requiredOneOfEvents().asJava.map { it.asJava }.toSet() -val CheckPointEvent.requiredEvents get() = this.requiredEvents().asJava -val Sieve.inputIngredients get() = this.inputIngredients().asJava -val Sieve.output get() = this.output().asJava + + diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt index 08fa4702c..008fd389d 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt @@ -1,17 +1,22 @@ package com.ing.baker.compiler import scala.`$less$colon$less` +import scala.`None$` +import scala.Option import scala.Predef +import scala.Some import scala.Tuple2 import scala.collection.immutable.ArraySeq import scala.collection.immutable.Seq import scala.collection.immutable.Vector +import scala.concurrent.duration.FiniteDuration import scala.jdk.CollectionConverters.ListHasAsScala import scala.jdk.CollectionConverters.MapHasAsJava import scala.jdk.CollectionConverters.MapHasAsScala import scala.jdk.CollectionConverters.SeqHasAsJava import scala.jdk.CollectionConverters.SetHasAsJava import scala.jdk.CollectionConverters.SetHasAsScala +import kotlin.time.Duration object ScalaConversions { @@ -26,4 +31,17 @@ object ScalaConversions { val Seq.asJava get(): List = SeqHasAsJava(this).asJava() val scala.collection.immutable.Set.asJava get(): Set = SetHasAsJava(this).asJava() val scala.collection.immutable.Map.asJava get(): Map = MapHasAsJava(this).asJava() + + val Duration?.asScala get(): Option = + when (this) { + null -> `None$`.`MODULE$` as Option + else -> Some(FiniteDuration(this.inWholeNanoseconds, java.util.concurrent.TimeUnit.NANOSECONDS)) + } + + val Int?.asScala get(): Option = + when (this) { + null -> `None$`.`MODULE$` as Option + else -> Some(this) + } + } diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt index 044a350ac..ab30595e7 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt @@ -2,4 +2,4 @@ package com.ing.baker.recipe import com.ing.baker.types.Type -data class Ingredient(val name: String, val type: Type) \ No newline at end of file +data class Ingredient(override val name: String, override val type: Type): IngredientDescriptor \ No newline at end of file diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt new file mode 100644 index 000000000..d33a65ce3 --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt @@ -0,0 +1,8 @@ +package com.ing.baker.recipe + +import com.ing.baker.types.Type + +interface IngredientDescriptor { + val name: String + val type: Type +} diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt index 33155e3ea..49f3a5bad 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt @@ -5,72 +5,19 @@ import com.ing.baker.types.Converters import com.ing.baker.types.Value data class Interaction( - val name: String, - val originalName: String, - val inputIngredients: List, - val output: List, - val requiredEvents: Set, - val requiredOneOfEvents: Set>, - val predefinedIngredients: Map, - val overriddenIngredientNames: Map, - val overriddenOutputIngredientName: String? = null, - val eventOutputTransformers: Map, - val maximumInteractionCount: Int? = null, - val failureStrategy: InteractionFailureStrategy? = null, - val isReprovider: Boolean -) { - companion object { - @JvmStatic - fun of( - name: String, - inputIngredients: List, - output: List - ): Interaction = - Interaction( - name, - originalName = name, - inputIngredients, - output, - requiredEvents = emptySet(), - requiredOneOfEvents = emptySet(), - predefinedIngredients = emptyMap(), - overriddenIngredientNames = emptyMap(), - overriddenOutputIngredientName = null, - eventOutputTransformers = emptyMap(), - maximumInteractionCount = null, - failureStrategy = null, - isReprovider = false - ) - - @JvmStatic - fun of( - name: String, - originalName: String, - inputIngredients: Set, - output: Set, - requiredEvents: Set, - requiredOneOfEvents: Set>, - predefinedIngredients: Map, - overriddenIngredientNames: Map, - eventOutputTransformers: Map, - maximumInteractionCount: Int?, - failureStrategy: InteractionFailureStrategy?, - isReprovider: Boolean - ): Interaction = - Interaction( - name, - originalName, - inputIngredients.toList(), - output.toList(), - requiredEvents, - requiredOneOfEvents, - predefinedIngredients.mapValues { Converters.toValue(it.value) }, - overriddenIngredientNames.toMap(), - overriddenOutputIngredientName = null, - eventOutputTransformers, - maximumInteractionCount, - failureStrategy, - isReprovider - ) - } -} \ No newline at end of file + override val name: String, + val oldName: String? = null, + override val inputIngredients: List = emptyList(), + override val output: List = emptyList(), + override val requiredEvents: Set = emptySet(), + override val requiredOneOfEvents: Set> = emptySet(), + override val predefinedIngredients: Map = emptyMap(), + override val overriddenIngredientNames: Map = emptyMap(), + override val overriddenOutputIngredientName: String? = null, + override val eventOutputTransformers: Map = emptyMap(), + override val maximumInteractionCount: Int? = null, + override val failureStrategy: InteractionFailureStrategy? = null, + override val isReprovider: Boolean = false, +) : InteractionDescriptor { + override val originalName get() = oldName ?: name +} diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt new file mode 100644 index 000000000..cf30d33fe --- /dev/null +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt @@ -0,0 +1,20 @@ +package com.ing.baker.recipe + +import com.ing.baker.recipe.common.InteractionFailureStrategy +import com.ing.baker.types.Value + +interface InteractionDescriptor { + val name: String + val originalName: String + val inputIngredients: List + val output: List + val requiredEvents: Set + val requiredOneOfEvents: Set> + val predefinedIngredients: Map + val overriddenIngredientNames: Map + val overriddenOutputIngredientName: String? + val eventOutputTransformers: Map + val maximumInteractionCount: Int? + val failureStrategy: InteractionFailureStrategy? + val isReprovider: Boolean +} \ No newline at end of file diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt index 01c6317f1..3ce964b0e 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt @@ -35,7 +35,7 @@ fun ScalaRecipe.toKotlin(): Recipe = fun ScalaInteraction.toKotlin(): Interaction = Interaction( name = name().orEmpty(), - originalName = originalName().orEmpty(), + oldName = originalName().orEmpty(), inputIngredients = inputIngredients().toKotlinIngredients(), output = output().toKotlinEvents(), requiredEvents = requiredEvents().toKotlin(), diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt index cc84781b5..ba19bd0a4 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt @@ -155,9 +155,9 @@ class RecipeCompilerBasicTest { val actual = convertList(compiled.petriNet().transitions()) .filterIsInstance() .map { it.interactionName() } - .toList() + .toSet() - val expected = listOf( + val expected = setOf( $$"$CheckpointEventInteraction$subSubSubCheckpointEvent", $$"$SubRecipe$SubRecipe$InteractionA", $$"$CheckpointEventInteraction$subSubCheckpointEvent", diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt index d1ab4db7b..57076df61 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt @@ -805,7 +805,7 @@ class RecipeCompilerFeaturesTest { } .filter { it.startsWith(subRecipePrefix) }.toSet() - assertEquals("5fdfb7538c687289", compiledRecipe.recipeId()) + assertEquals("52680e1ab514fa5e", compiledRecipe.recipeId()) assertEquals( setOf( $$"$SubRecipe$SubSubRecipe$InteractionOne", diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt index ecfebaced..08b55cbf3 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt @@ -7,6 +7,7 @@ import com.ing.baker.recipe.annotations.RequiresIngredient import com.ing.baker.recipe.javadsl.Interaction import com.ing.baker.recipe.javadsl.InteractionDescriptor import com.ing.baker.recipe.javadsl.Recipe +import com.ing.baker.recipe.toKotlin import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import scala.jdk.javaapi.CollectionConverters @@ -58,12 +59,12 @@ class RecipeCompilerUnitTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) assertEquals(2, actionDescriptors.size, "Should extract 2 interactions") assertEquals(1, sensoryEvents.count(), "Should extract 1 sensory event") - assertTrue(actionDescriptors.any { it.name() == "ReserveItems" }) - assertTrue(actionDescriptors.any { it.name() == "ProcessPayment" }) + assertTrue(actionDescriptors.any { it.name == "ReserveItems" }) + assertTrue(actionDescriptors.any { it.name == "ProcessPayment" }) } @Test @@ -72,7 +73,7 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (_, _, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (_, _, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) assertTrue(allIngredientNames.contains("orderId"), "Should include orderId from OrderPlaced") assertTrue(allIngredientNames.contains("items"), "Should include items from OrderPlaced") @@ -83,7 +84,7 @@ class RecipeCompilerUnitTest { fun `prepareRecipeComponents handles empty recipe`() { val recipe = Recipe("EmptyRecipe") - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) assertEquals(0, actionDescriptors.size) assertEquals(0, sensoryEvents.count()) @@ -96,7 +97,7 @@ class RecipeCompilerUnitTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -115,7 +116,7 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withSensoryEvent(PaymentMade::class.java) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -135,7 +136,7 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -155,7 +156,7 @@ class RecipeCompilerUnitTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -177,7 +178,7 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -195,7 +196,7 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -214,7 +215,7 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -224,7 +225,7 @@ class RecipeCompilerUnitTest { val arcs = RecipeCompiler.buildAllPetriNetArcs(actionDescriptors, transitions) val compiledRecipe = RecipeCompiler.assemblePetriNetAndValidate( - recipe, + recipe.toKotlin(), arcs, emptyList(), emptyList(), @@ -242,7 +243,7 @@ class RecipeCompilerUnitTest { val recipe = Recipe("TestRecipe") .withSensoryEvent(OrderPlaced::class.java) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) val transitions = RecipeCompiler.buildAllTransitions( actionDescriptors, allIngredientNames, @@ -254,7 +255,7 @@ class RecipeCompilerUnitTest { val precompileErrors = listOf("Test error 1") val preconditionErrors = listOf("Test error 2") val compiledRecipe = RecipeCompiler.assemblePetriNetAndValidate( - recipe, + recipe.toKotlin(), arcs, precompileErrors, preconditionErrors, diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt index 7b51f7b9e..5f19fcca0 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt +++ b/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt @@ -5,11 +5,13 @@ import com.ing.baker.il.EventDescriptor import com.ing.baker.il.IngredientDescriptor import com.ing.baker.il.petrinet.EventTransition import com.ing.baker.il.petrinet.InteractionTransition -import com.ing.baker.recipe.common.Event -import com.ing.baker.recipe.common.Ingredient -import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.recipe.Interaction +import com.ing.baker.recipe.Event +import com.ing.baker.recipe.Ingredient +import com.ing.baker.recipe.InteractionDescriptor import com.ing.baker.recipe.common.InteractionFailureStrategy import com.ing.baker.types.`Int32$` +import com.ing.baker.types.Value import scala.Option /** @@ -85,28 +87,28 @@ object TestDataBuilders { val events = outputEventNames.map { eventName -> val eventIngredients = outputEventIngredients[eventName] ?: emptyList() val ingredientList = eventIngredients.map { Ingredient(it, `Int32$`.`MODULE$`) } - com.ing.baker.recipe.scaladsl.Event( + Event( eventName, - ingredientList.asScala, - Option.empty() + ingredientList, + null ) } // Create minimal InteractionDescriptor using Interaction.apply - val descriptor = com.ing.baker.recipe.scaladsl.Interaction.apply( + val descriptor = Interaction( name, - ingredients.asScala, - events.asScala, - emptySet().asScala, - emptySet>().asScala, - emptyMap().asScala, - emptyMap().asScala, - Option.empty(), - Option.empty(), - Option.empty(), - emptyMap().asScala, + null, + ingredients, + events, + emptySet(), + emptySet>(), + emptyMap(), + emptyMap(), + null, + emptyMap(), + null, + null, false, - Option.empty() ) // Reuse RecipeCompiler.interactionTransitionOf @@ -154,22 +156,20 @@ object TestDataBuilders { requiredEvents: Set = emptySet(), requiredOneOfEvents: Set> = emptySet() ): InteractionDescriptor { - val scalaRequiredOneOfEvents = requiredOneOfEvents.map { it.asScala }.toSet().asScala - - return com.ing.baker.recipe.scaladsl.Interaction.apply( + return Interaction( name, - emptyList().asScala, - emptyList().asScala, - requiredEvents.asScala, - scalaRequiredOneOfEvents, - emptyMap().asScala, - emptyMap().asScala, - Option.empty(), - Option.empty(), - Option.empty(), - emptyMap().asScala, - false, - Option.empty() + null, + emptyList(), + emptyList(), + requiredEvents, + requiredOneOfEvents, + emptyMap(), + emptyMap(), + null, + emptyMap(), + null, + null, + false ) } } diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala index e05d04ed4..b4690e417 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala @@ -111,7 +111,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List.empty) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe MissingImplementation:e1b92d7afa5609d5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) + case Left(e) => e should have('message("Recipe MissingImplementation:224cacdbf4b4e350 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) case Right(_) => fail("Adding a recipe should fail") } } yield succeed @@ -126,7 +126,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(new InteractionOneWrongApply()))) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe WrongImplementation:7fcdb6b8feb8aa87 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) + case Left(e) => e should have('message("Recipe WrongImplementation:165501eaeefc4b2f has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) case Right(_) => fail("Adding an interaction should fail") } } yield succeed From 70c1bebbd06a80fb666eb8a2fde80497cd715d21 Mon Sep 17 00:00:00 2001 From: Elmar Wachtmeester Date: Mon, 18 May 2026 12:55:54 +0200 Subject: [PATCH 11/16] Feat/split baker akka runtime (#1942) * Extracted baker-recipe-manager module from baker-akka-runtime * Extracted Timeouts from AkkaBakerConfig * Extracted akka baker actor system from akka baker runtime --- .../components/AkkaBakeryComponents.scala | 6 +- core/baker-akka-actors/pom.xml | 239 ++++++++++++++++++ .../main/protobuf/delayed_transition.proto | 0 .../src/main/protobuf/process_index.proto | 0 .../src/main/protobuf/process_instance.proto | 0 .../src/main/protobuf/recipe_manager.proto | 0 .../akka/actor/BakerActorCleanup.scala | 0 .../akka/actor/BakerActorProvider.scala | 0 .../actor/ClusterBakerActorProvider.scala | 2 - .../runtime/akka/actor/GracefulShutdown.scala | 0 .../actor/GracefulShutdownShardRegions.scala | 0 .../akka/actor/LocalBakerActorProvider.scala | 3 +- .../baker/runtime/akka/actor/Timeouts.scala | 39 +++ .../ing/baker/runtime/akka/actor/Util.scala | 0 .../DelayedTransitionActor.scala | 0 .../DelayedTransitionActorProtocol.scala | 0 .../DelayedTranstionProto.scala | 0 .../akka/actor/logging/LogAndSendEvent.scala | 0 .../actor/process_index/ProcessIndex.scala | 0 .../process_index/ProcessIndexProto.scala | 0 .../process_index/ProcessIndexProtocol.scala | 0 .../SensoryEventResponseHandler.scala | 0 .../process_instance/ProcessInstance.scala | 0 .../ProcessInstanceEventSourcing.scala | 0 .../ProcessInstanceLogger.scala | 0 .../ProcessInstanceProto.scala | 0 .../ProcessInstanceProtocol.scala | 0 .../ProcessInstanceRuntime.scala | 0 .../ProcessInstanceSerialization.scala | 0 .../internal/ExceptionState.scala | 0 .../internal/ExceptionStrategy.scala | 0 .../process_instance/internal/Instance.scala | 0 .../actor/process_instance/internal/Job.scala | 0 .../recipe_manager/RecipeManagerActor.scala | 0 .../recipe_manager/RecipeManagerProto.scala | 0 .../RecipeManagerProtocol.scala | 0 .../AkkaSerializerProvider.scala | 0 .../serialization/BakerSerializable.scala | 0 .../BakerTypedProtobufSerializer.scala | 0 .../serialization/SerializedDataProto.scala | 0 .../TypedProtobufSerializer.scala | 0 .../internal/CachingInteractionManager.scala | 0 .../internal/FatalInteractionException.scala | 0 .../runtime/akka/internal/RecipeRuntime.scala | 0 .../runtime/akka/internal/TimeoutUtil.scala | 0 .../runtime/akka/journal/SinkJournal.scala | 0 .../com/ing/baker/runtime/akka/package.scala | 0 .../ActorBasedRecipeManager.scala | 2 +- .../journal/CURRENT | 0 .../persisted-messages-windows/journal/LOCK | 0 .../journal/MANIFEST-000002 | Bin .../persisted-messages/journal/CURRENT | 0 .../resources/persisted-messages/journal/LOCK | 0 .../journal/MANIFEST-000002 | Bin .../src/test/resources/reference.conf | 140 ++++++++++ .../runtime/akka/actor/AkkaTestBase.scala | 0 .../baker/runtime/akka/actor/UtilSpec.scala | 0 .../DelayedTransitionActorSpec.scala | 0 .../process_index/ProcessIndexSpec.scala | 0 .../SensoryEventResponseHandlerSpec.scala | 0 .../ProcessInstanceEventSourcingSpec.scala | 0 .../ProcessInstanceSpec.scala | 0 .../process_instance/dsl/SequenceNet.scala | 0 .../dsl/StateTransition.scala | 0 .../dsl/StateTransitionNet.scala | 0 .../process_instance/dsl/TestUtils.scala | 0 .../actor/process_instance/dsl/package.scala | 0 .../EncryptionPropertiesSpec.scala | 0 .../internal/InteractionManagerSpec.scala | 0 .../akka/internal/RecipeRuntimeSpec.scala | 0 .../journal/SinkJournalAndSnapshotSpec.scala | 0 .../ActorBasedRecipeManagerSpec.scala | 2 +- core/baker-akka-runtime/pom.xml | 177 +------------ .../{test => main}/resources/application.conf | 0 .../baker/runtime/akka/AkkaBakerConfig.scala | 37 +-- .../test/java/com/ing/baker/BakerTest.java | 6 +- .../compiler/JavaCompiledRecipeTest.java | 2 +- .../scala/com/ing/baker/AllTypeRecipe.scala | 1 - .../com/ing/baker/runtime/ExamplesSpec.scala | 12 +- .../ing/baker/runtime/ScalaDSLRuntime.scala | 2 +- .../baker/runtime/akka/BakerEventsSpec.scala | 3 +- .../runtime/akka/BakerExecutionSpec.scala | 10 +- .../RecipeManagerActorSpec.scala | 1 - .../serialization/SerializationSpec.scala | 2 +- core/baker-recipe-manager/pom.xml | 51 ++++ .../recipe_manager/DefaultRecipeManager.scala | 0 .../runtime/recipe_manager/PollingAware.scala | 0 .../recipe_manager/RecipeManager.scala | 0 .../DefaultRecipeManagerSpec.scala | 0 .../recipe_manager/PollingAwareSpec.scala | 0 pom.xml | 2 + 91 files changed, 500 insertions(+), 239 deletions(-) create mode 100644 core/baker-akka-actors/pom.xml rename core/{baker-akka-runtime => baker-akka-actors}/src/main/protobuf/delayed_transition.proto (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/protobuf/process_index.proto (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/protobuf/process_instance.proto (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/protobuf/recipe_manager.proto (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorCleanup.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorProvider.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/ClusterBakerActorProvider.scala (98%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdown.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdownShardRegions.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/LocalBakerActorProvider.scala (95%) create mode 100644 core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/Timeouts.scala rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/Util.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActor.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorProtocol.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTranstionProto.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/logging/LogAndSendEvent.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndex.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProto.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProtocol.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandler.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstance.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcing.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceLogger.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProto.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProtocol.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceRuntime.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSerialization.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionState.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionStrategy.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Instance.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Job.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActor.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProto.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProtocol.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/AkkaSerializerProvider.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerSerializable.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerTypedProtobufSerializer.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/SerializedDataProto.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/TypedProtobufSerializer.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/internal/CachingInteractionManager.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/internal/FatalInteractionException.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/internal/RecipeRuntime.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/internal/TimeoutUtil.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/journal/SinkJournal.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/akka/package.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/main/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManager.scala (98%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/resources/persisted-messages-windows/journal/CURRENT (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/resources/persisted-messages-windows/journal/LOCK (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/resources/persisted-messages-windows/journal/MANIFEST-000002 (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/resources/persisted-messages/journal/CURRENT (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/resources/persisted-messages/journal/LOCK (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/resources/persisted-messages/journal/MANIFEST-000002 (100%) create mode 100644 core/baker-akka-actors/src/test/resources/reference.conf rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/AkkaTestBase.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/UtilSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcingSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/SequenceNet.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransition.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransitionNet.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/TestUtils.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/package.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/EncryptionPropertiesSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/internal/InteractionManagerSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/internal/RecipeRuntimeSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/akka/journal/SinkJournalAndSnapshotSpec.scala (100%) rename core/{baker-akka-runtime => baker-akka-actors}/src/test/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManagerSpec.scala (98%) rename core/baker-akka-runtime/src/{test => main}/resources/application.conf (100%) create mode 100644 core/baker-recipe-manager/pom.xml rename core/{baker-akka-runtime => baker-recipe-manager}/src/main/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManager.scala (100%) rename core/{baker-akka-runtime => baker-recipe-manager}/src/main/scala/com/ing/baker/runtime/recipe_manager/PollingAware.scala (100%) rename core/{baker-akka-runtime => baker-recipe-manager}/src/main/scala/com/ing/baker/runtime/recipe_manager/RecipeManager.scala (100%) rename core/{baker-akka-runtime => baker-recipe-manager}/src/test/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManagerSpec.scala (100%) rename core/{baker-akka-runtime => baker-recipe-manager}/src/test/scala/com/ing/baker/runtime/recipe_manager/PollingAwareSpec.scala (100%) diff --git a/bakery/bakery-state/src/main/scala/com/ing/bakery/components/AkkaBakeryComponents.scala b/bakery/bakery-state/src/main/scala/com/ing/bakery/components/AkkaBakeryComponents.scala index e7a33ebdc..9e8225d7a 100644 --- a/bakery/bakery-state/src/main/scala/com/ing/bakery/components/AkkaBakeryComponents.scala +++ b/bakery/bakery-state/src/main/scala/com/ing/bakery/components/AkkaBakeryComponents.scala @@ -3,7 +3,7 @@ package com.ing.bakery.components import akka.actor.ActorSystem import cats.effect.{IO, Resource} import com.ing.baker.runtime.akka.AkkaBakerConfig -import com.ing.baker.runtime.akka.actor.BakerActorProvider +import com.ing.baker.runtime.akka.actor.{BakerActorProvider, Timeouts} import com.ing.baker.runtime.model.InteractionManager import com.ing.baker.runtime.recipe_manager.{ActorBasedRecipeManager, RecipeManager} import com.ing.bakery.metrics.MetricService @@ -54,8 +54,8 @@ class AkkaBakeryComponents(optionalConfig: Option[Config] = None, def ec(actorSystem: ActorSystem): Resource[IO, ExecutionContext] = Resource.pure[IO, ExecutionContext](actorSystem.dispatcher) - def akkaBakerTimeoutsResource(config: Config): Resource[IO, AkkaBakerConfig.Timeouts] = - Resource.pure[IO, AkkaBakerConfig.Timeouts](AkkaBakerConfig.Timeouts(config)).logResourceLifecycle("Timeouts") + def akkaBakerTimeoutsResource(config: Config): Resource[IO, Timeouts] = + Resource.pure[IO, Timeouts](Timeouts(config)).logResourceLifecycle("Timeouts") def akkaBakerConfigValidationSettingsResource(config: Config): Resource[IO, AkkaBakerConfig.BakerValidationSettings] = Resource.pure[IO, AkkaBakerConfig.BakerValidationSettings](AkkaBakerConfig.BakerValidationSettings.from(config)).logResourceLifecycle("BakerValidationSettings") diff --git a/core/baker-akka-actors/pom.xml b/core/baker-akka-actors/pom.xml new file mode 100644 index 000000000..95d1e8342 --- /dev/null +++ b/core/baker-akka-actors/pom.xml @@ -0,0 +1,239 @@ + + + 4.0.0 + + + com.ing.baker + baker + 5.1.0-SNAPSHOT + ../../pom.xml + + + baker-akka-actors + Baker Akka Actor System + Akka-based Actor System for Baker + + + + com.ing.baker + baker-recipe-manager + ${project.version} + + + com.ing.baker + baker-recipe-dsl + ${project.version} + test-jar + test + + + + org.scala-lang + scala-library + + + + + com.typesafe.akka + akka-actor_${scala.compat.version} + + + com.typesafe.akka + akka-stream_${scala.compat.version} + + + com.typesafe.akka + akka-protobuf-v3_${scala.compat.version} + + + com.typesafe.akka + akka-persistence_${scala.compat.version} + + + com.typesafe.akka + akka-persistence-query_${scala.compat.version} + + + com.typesafe.akka + akka-cluster_${scala.compat.version} + + + com.typesafe.akka + akka-cluster-tools_${scala.compat.version} + + + com.typesafe.akka + akka-cluster-sharding_${scala.compat.version} + + + com.lightbend.akka.management + akka-management-cluster-bootstrap_${scala.compat.version} + + + com.typesafe.akka + akka-discovery_${scala.compat.version} + + + com.github.dnvriend + akka-persistence-inmemory_${scala.compat.version} + + + com.typesafe.akka + akka-actor-typed_${scala.compat.version} + + + com.typesafe.akka + akka-cluster-typed_${scala.compat.version} + + + com.typesafe.akka + akka-persistence-typed_${scala.compat.version} + + + com.typesafe.akka + akka-stream-typed_${scala.compat.version} + + + com.typesafe.akka + akka-slf4j_${scala.compat.version} + + + + + com.iheart + ficus_${scala.compat.version} + + + org.typelevel + cats-core_${scala.compat.version} + + + org.typelevel + cats-effect_${scala.compat.version} + + + com.thesamet.scalapb + scalapb-runtime_${scala.compat.version} + + + com.google.protobuf + protobuf-java + + + org.slf4j + slf4j-api + + + com.typesafe.scala-logging + scala-logging_${scala.compat.version} + + + nl.pragmasoft.sensors + sensors-core_${scala.compat.version} + + + + + + + + + + + + com.ing.baker + baker-compiler + ${project.version} + test + + + com.typesafe.akka + akka-testkit_${scala.compat.version} + test + + + com.typesafe.akka + akka-stream-testkit_${scala.compat.version} + test + + + org.scalatest + scalatest_${scala.compat.version} + test + + + org.scalacheck + scalacheck_${scala.compat.version} + test + + + org.scalatestplus + scalacheck-1-17_${scala.compat.version} + test + + + org.scalatestplus + mockito-3-12_${scala.compat.version} + test + + + + + + + net.alchim31.maven + scala-maven-plugin + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + ${basedir}/src/main/protobuf + ${basedir}/target/generated-sources/protobuf/scala + false + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + + + scalapb + com.thesamet.scalapb + compilerplugin_${scala.compat.version} + ${scalapb.version} + scalapb.ScalaPbCodeGenerator + + + + + + + compile + + + + + + + org.scalatest + scalatest-maven-plugin + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + create-test-jar + package + + test-jar + + + + + + + diff --git a/core/baker-akka-runtime/src/main/protobuf/delayed_transition.proto b/core/baker-akka-actors/src/main/protobuf/delayed_transition.proto similarity index 100% rename from core/baker-akka-runtime/src/main/protobuf/delayed_transition.proto rename to core/baker-akka-actors/src/main/protobuf/delayed_transition.proto diff --git a/core/baker-akka-runtime/src/main/protobuf/process_index.proto b/core/baker-akka-actors/src/main/protobuf/process_index.proto similarity index 100% rename from core/baker-akka-runtime/src/main/protobuf/process_index.proto rename to core/baker-akka-actors/src/main/protobuf/process_index.proto diff --git a/core/baker-akka-runtime/src/main/protobuf/process_instance.proto b/core/baker-akka-actors/src/main/protobuf/process_instance.proto similarity index 100% rename from core/baker-akka-runtime/src/main/protobuf/process_instance.proto rename to core/baker-akka-actors/src/main/protobuf/process_instance.proto diff --git a/core/baker-akka-runtime/src/main/protobuf/recipe_manager.proto b/core/baker-akka-actors/src/main/protobuf/recipe_manager.proto similarity index 100% rename from core/baker-akka-runtime/src/main/protobuf/recipe_manager.proto rename to core/baker-akka-actors/src/main/protobuf/recipe_manager.proto diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorCleanup.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorCleanup.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorCleanup.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorCleanup.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorProvider.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorProvider.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorProvider.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/BakerActorProvider.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/ClusterBakerActorProvider.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/ClusterBakerActorProvider.scala similarity index 98% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/ClusterBakerActorProvider.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/ClusterBakerActorProvider.scala index 653b7ce17..eb825a3de 100644 --- a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/ClusterBakerActorProvider.scala +++ b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/ClusterBakerActorProvider.scala @@ -10,7 +10,6 @@ import akka.util.Timeout import cats.data.NonEmptyList import cats.effect.IO import com.ing.baker.il.sha256HashCode -import com.ing.baker.runtime.akka.AkkaBakerConfig import com.ing.baker.runtime.akka.actor.ClusterBakerActorProvider._ import com.ing.baker.runtime.akka.actor.process_index.ProcessIndex.{ActorMetadata, StopProcessIndexShard} import com.ing.baker.runtime.akka.actor.process_index.ProcessIndexProtocol._ @@ -72,7 +71,6 @@ class ClusterBakerActorProvider( getIngredientsFilter: List[String], providedIngredientFilter: List[String], configuredEncryption: Encryption, - timeouts: AkkaBakerConfig.Timeouts, blacklistedProcesses: List[String], rememberProcessDuration: Option[Duration]) extends BakerActorProvider with LazyLogging { diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdown.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdown.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdown.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdown.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdownShardRegions.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdownShardRegions.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdownShardRegions.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/GracefulShutdownShardRegions.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/LocalBakerActorProvider.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/LocalBakerActorProvider.scala similarity index 95% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/LocalBakerActorProvider.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/LocalBakerActorProvider.scala index e9dee6797..a4f4976de 100644 --- a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/LocalBakerActorProvider.scala +++ b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/LocalBakerActorProvider.scala @@ -3,7 +3,7 @@ package com.ing.baker.runtime.akka.actor import akka.actor.{ActorRef, ActorSystem} import akka.pattern.{BackoffOpts, BackoffSupervisor} import cats.effect.IO -import com.ing.baker.runtime.akka.{AkkaBakerConfig, _} +import com.ing.baker.runtime.akka._ import com.ing.baker.runtime.akka.actor.process_index.ProcessIndex import com.ing.baker.runtime.akka.actor.process_index.ProcessIndex.ActorMetadata import com.ing.baker.runtime.akka.actor.process_index.ProcessIndexProtocol.{GetIndex, Index} @@ -21,7 +21,6 @@ class LocalBakerActorProvider( providedIngredientFilter: List[String], actorIdleTimeout: Option[FiniteDuration], configuredEncryption: Encryption, - timeouts: AkkaBakerConfig.Timeouts, blacklistedProcesses: List[String], rememberProcessDuration: Option[Duration] ) extends BakerActorProvider { diff --git a/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/Timeouts.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/Timeouts.scala new file mode 100644 index 000000000..ce937a566 --- /dev/null +++ b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/Timeouts.scala @@ -0,0 +1,39 @@ +package com.ing.baker.runtime.akka.actor + +import com.typesafe.config.Config + +import scala.concurrent.duration.FiniteDuration +import net.ceedubs.ficus.Ficus._ + +import scala.concurrent.duration._ + +case class Timeouts(defaultBakeTimeout: FiniteDuration, + defaultProcessEventTimeout: FiniteDuration, + defaultInquireTimeout: FiniteDuration, + defaultShutdownTimeout: FiniteDuration, + defaultAddRecipeTimeout: FiniteDuration, + defaultExecuteSingleInteractionTimeout: FiniteDuration) + +object Timeouts { + + def default: Timeouts = + Timeouts( + defaultBakeTimeout = 10.seconds, + defaultProcessEventTimeout = 10.seconds, + defaultInquireTimeout = 10.seconds, + defaultShutdownTimeout = 30.seconds, + defaultAddRecipeTimeout = 10.seconds, + defaultExecuteSingleInteractionTimeout = 60.seconds, + ) + + def apply(config: Config): Timeouts = + Timeouts( + defaultBakeTimeout = config.as[FiniteDuration]("baker.bake-timeout"), + defaultProcessEventTimeout = config.as[FiniteDuration]("baker.process-event-timeout"), + defaultInquireTimeout = config.as[FiniteDuration]("baker.process-inquire-timeout"), + defaultShutdownTimeout = config.as[FiniteDuration]("baker.shutdown-timeout"), + defaultAddRecipeTimeout = config.as[FiniteDuration]("baker.add-recipe-timeout"), + defaultExecuteSingleInteractionTimeout = config.as[FiniteDuration]("baker.execute-single-interaction-timeout"), + ) +} + diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/Util.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/Util.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/Util.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/Util.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActor.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActor.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActor.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActor.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorProtocol.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorProtocol.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorProtocol.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorProtocol.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTranstionProto.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTranstionProto.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTranstionProto.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTranstionProto.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/logging/LogAndSendEvent.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/logging/LogAndSendEvent.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/logging/LogAndSendEvent.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/logging/LogAndSendEvent.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndex.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndex.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndex.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndex.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProto.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProto.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProto.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProto.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProtocol.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProtocol.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProtocol.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexProtocol.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandler.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandler.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandler.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandler.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstance.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstance.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstance.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstance.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcing.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcing.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcing.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcing.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceLogger.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceLogger.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceLogger.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceLogger.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProto.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProto.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProto.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProto.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProtocol.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProtocol.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProtocol.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceProtocol.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceRuntime.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceRuntime.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceRuntime.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceRuntime.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSerialization.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSerialization.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSerialization.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSerialization.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionState.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionState.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionState.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionState.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionStrategy.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionStrategy.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionStrategy.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/ExceptionStrategy.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Instance.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Instance.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Instance.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Instance.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Job.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Job.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Job.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/process_instance/internal/Job.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActor.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActor.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActor.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActor.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProto.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProto.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProto.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProto.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProtocol.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProtocol.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProtocol.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerProtocol.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/AkkaSerializerProvider.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/AkkaSerializerProvider.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/AkkaSerializerProvider.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/AkkaSerializerProvider.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerSerializable.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerSerializable.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerSerializable.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerSerializable.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerTypedProtobufSerializer.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerTypedProtobufSerializer.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerTypedProtobufSerializer.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/BakerTypedProtobufSerializer.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/SerializedDataProto.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/SerializedDataProto.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/SerializedDataProto.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/SerializedDataProto.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/TypedProtobufSerializer.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/TypedProtobufSerializer.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/TypedProtobufSerializer.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/actor/serialization/TypedProtobufSerializer.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/CachingInteractionManager.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/CachingInteractionManager.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/CachingInteractionManager.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/CachingInteractionManager.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/FatalInteractionException.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/FatalInteractionException.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/FatalInteractionException.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/FatalInteractionException.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/RecipeRuntime.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/RecipeRuntime.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/RecipeRuntime.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/RecipeRuntime.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/TimeoutUtil.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/TimeoutUtil.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/internal/TimeoutUtil.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/internal/TimeoutUtil.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/journal/SinkJournal.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/journal/SinkJournal.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/journal/SinkJournal.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/journal/SinkJournal.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/package.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/package.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/package.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/akka/package.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManager.scala b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManager.scala similarity index 98% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManager.scala rename to core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManager.scala index d3cb17f79..2e90e014a 100644 --- a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManager.scala +++ b/core/baker-akka-actors/src/main/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManager.scala @@ -5,8 +5,8 @@ import _root_.akka.pattern._ import _root_.akka.util.Timeout import akka.cluster.Cluster import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings, ClusterSingletonProxy, ClusterSingletonProxySettings} -import com.ing.baker.runtime.akka.AkkaBakerConfig._ import com.ing.baker.runtime.akka.actor.ClusterBakerActorProvider.recipeManagerName +import com.ing.baker.runtime.akka.actor.Timeouts import com.ing.baker.runtime.akka.actor.recipe_manager.RecipeManagerActor import com.ing.baker.runtime.akka.actor.recipe_manager.RecipeManagerProtocol._ import com.ing.baker.runtime.common.RecipeRecord diff --git a/core/baker-akka-runtime/src/test/resources/persisted-messages-windows/journal/CURRENT b/core/baker-akka-actors/src/test/resources/persisted-messages-windows/journal/CURRENT similarity index 100% rename from core/baker-akka-runtime/src/test/resources/persisted-messages-windows/journal/CURRENT rename to core/baker-akka-actors/src/test/resources/persisted-messages-windows/journal/CURRENT diff --git a/core/baker-akka-runtime/src/test/resources/persisted-messages-windows/journal/LOCK b/core/baker-akka-actors/src/test/resources/persisted-messages-windows/journal/LOCK similarity index 100% rename from core/baker-akka-runtime/src/test/resources/persisted-messages-windows/journal/LOCK rename to core/baker-akka-actors/src/test/resources/persisted-messages-windows/journal/LOCK diff --git a/core/baker-akka-runtime/src/test/resources/persisted-messages-windows/journal/MANIFEST-000002 b/core/baker-akka-actors/src/test/resources/persisted-messages-windows/journal/MANIFEST-000002 similarity index 100% rename from core/baker-akka-runtime/src/test/resources/persisted-messages-windows/journal/MANIFEST-000002 rename to core/baker-akka-actors/src/test/resources/persisted-messages-windows/journal/MANIFEST-000002 diff --git a/core/baker-akka-runtime/src/test/resources/persisted-messages/journal/CURRENT b/core/baker-akka-actors/src/test/resources/persisted-messages/journal/CURRENT similarity index 100% rename from core/baker-akka-runtime/src/test/resources/persisted-messages/journal/CURRENT rename to core/baker-akka-actors/src/test/resources/persisted-messages/journal/CURRENT diff --git a/core/baker-akka-runtime/src/test/resources/persisted-messages/journal/LOCK b/core/baker-akka-actors/src/test/resources/persisted-messages/journal/LOCK similarity index 100% rename from core/baker-akka-runtime/src/test/resources/persisted-messages/journal/LOCK rename to core/baker-akka-actors/src/test/resources/persisted-messages/journal/LOCK diff --git a/core/baker-akka-runtime/src/test/resources/persisted-messages/journal/MANIFEST-000002 b/core/baker-akka-actors/src/test/resources/persisted-messages/journal/MANIFEST-000002 similarity index 100% rename from core/baker-akka-runtime/src/test/resources/persisted-messages/journal/MANIFEST-000002 rename to core/baker-akka-actors/src/test/resources/persisted-messages/journal/MANIFEST-000002 diff --git a/core/baker-akka-actors/src/test/resources/reference.conf b/core/baker-akka-actors/src/test/resources/reference.conf new file mode 100644 index 000000000..a066d2d41 --- /dev/null +++ b/core/baker-akka-actors/src/test/resources/reference.conf @@ -0,0 +1,140 @@ +baker { + + actor { + # either "local" or "cluster-sharded" + provider = "local" + + # the recommended nr is number-of-cluster-nodes * 10 + cluster.nr-of-shards = 50 + + # the time that inactive actors (processes) stay in memory + idle-timeout = 5 minutes + + # The interval that a check is done of processes should be deleted + retention-check-interval = 1 minutes + + # The interval of messages between creating snapshots for the actors + snapshot-interval = 100 + + # The amount of snapshots to keep for the actors + snapshot-count = 3 + } + + # the default timeout for Baker.bake(..) process creation calls + bake-timeout = 10 seconds + + # the timeout for refreshing the local recipe cache + process-index-update-cache-timeout = 5 seconds + + # the default timeout for Baker.processEvent(..) + process-event-timeout = 10 seconds + + # the default timeout for inquires on Baker, this means getIngredients(..) & getEvents(..) + process-inquire-timeout = 10 seconds + + # when baker starts up, it attempts to 'initialize' the journal connection, this may take some time + journal-initialize-timeout = 30 seconds + + # the default timeout for adding a recipe to Baker + add-recipe-timeout = 10 seconds + + # the time to wait for a graceful shutdown + shutdown-timeout = 30 seconds + + # the timeout when calling executeSingleInteraction + execute-single-interaction-timeout = 60 seconds + + process-index { + # Configurations for the restart policy for the ProcessIndex actor + restart-minBackoff = 1 seconds + restart-maxBackoff = 30 seconds + restart-randomFactor = 0.2 + start-all-shards = true + remember-process-duration = 30 days + } + + process-instance { + # Configurations for the restart policy for the ProcessInstance actor + restart-minBackoff = 1 seconds + restart-maxBackoff = 30 seconds + restart-randomFactor = 0.2 + } + + delayed-transition { + # Configurations for the restart policy for the DelayedTransition actor + restart-minBackoff = 1 seconds + restart-maxBackoff = 30 seconds + restart-randomFactor = 0.2 + } + + # process-instances that should not be loaded and will be deleted on startup + blacklisted-processes = [] + + # The ingredients that are filtered out when getting the process instance. + # This should be used if there are big ingredients to improve performance and memory usage. + # The ingredients will be in the ingredients map but there value will be an empty String. + filtered-ingredient-values = [] + + # Values to filter specifically for the GetIngredients call, this is combined with the filtered-ingredient-values + filtered-ingredient-values-for-get = [] + + # Values to filter specifically used for the internal event stream, this is combined with the filtered-ingredient-values + filtered-ingredient-values-for-stream = [] + + # Determines if a recipe can be added if not all needed interaction instances are available. + allow-adding-recipe-without-requiring-instances = false + + # encryption settings + encryption { + + # whether to encrypt data stored in the journal, off or on + enabled = off + + # if enabled = on, a secret should be set + # secret = ??? + } + + # leave blank for local baker, put implementation class for interaction discovery in bakery + interactions.class = "" + + # If this is set to true it will also allow fur supersets of the output types to be given by the implementations + # This can be helpful in case an ENUM type or similar is extended upon and you know these new values will not be given. + # If this new value is given from the implementation this will result in te runtime error and a technical failure of the interaction. + interactions.allow-superset-for-output-types = false + interactions.kubernetes.pod-label-selector = "" + interactions.localhost.port = 8081 +} + +akka { + + # by default we use the in memory journal from: https://github.com/dnvriend/akka-persistence-inmemory + persistence.journal.plugin = "inmemory-journal" + persistence.snapshot-store.plugin = "inmemory-snapshot-store" + + actor { + + serializers { + + baker-typed-protobuf = "com.ing.baker.runtime.akka.actor.serialization.BakerTypedProtobufSerializer" + + } + + serialization-bindings { + + "com.ing.baker.runtime.akka.actor.serialization.BakerSerializable" = baker-typed-protobuf + + "com.ing.baker.types.Value" = baker-typed-protobuf + "com.ing.baker.types.Type" = baker-typed-protobuf + "com.ing.baker.il.CompiledRecipe" = baker-typed-protobuf + "com.ing.baker.runtime.scaladsl.EventInstance" = baker-typed-protobuf + "com.ing.baker.runtime.scaladsl.RecipeInstanceState" = baker-typed-protobuf + "com.ing.baker.runtime.scaladsl.RecipeEventMetadata" = baker-typed-protobuf + + // Baker messages are scalapb generated and also tagged with the BakerSerializable trait. + // scalapb.GeneratedMessage is mapped to akka proto serializer in the akka reference config + // which creates ambiguity together with our config. This setting is to ensure that akka picks the correct + // serializer for the baker messages. + "scalapb.GeneratedMessage" = none + } + } +} diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/AkkaTestBase.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/AkkaTestBase.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/AkkaTestBase.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/AkkaTestBase.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/UtilSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/UtilSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/UtilSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/UtilSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/delayed_transition_actor/DelayedTransitionActorSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/ProcessIndexSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_index/SensoryEventResponseHandlerSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcingSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcingSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcingSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceEventSourcingSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/ProcessInstanceSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/SequenceNet.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/SequenceNet.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/SequenceNet.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/SequenceNet.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransition.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransition.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransition.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransition.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransitionNet.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransitionNet.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransitionNet.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/StateTransitionNet.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/TestUtils.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/TestUtils.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/TestUtils.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/TestUtils.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/package.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/package.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/package.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/process_instance/dsl/package.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/EncryptionPropertiesSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/EncryptionPropertiesSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/EncryptionPropertiesSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/EncryptionPropertiesSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/internal/InteractionManagerSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/internal/InteractionManagerSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/internal/InteractionManagerSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/internal/InteractionManagerSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/internal/RecipeRuntimeSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/internal/RecipeRuntimeSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/internal/RecipeRuntimeSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/internal/RecipeRuntimeSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/journal/SinkJournalAndSnapshotSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/journal/SinkJournalAndSnapshotSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/journal/SinkJournalAndSnapshotSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/akka/journal/SinkJournalAndSnapshotSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManagerSpec.scala b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManagerSpec.scala similarity index 98% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManagerSpec.scala rename to core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManagerSpec.scala index 75e9fd9b1..c121998e5 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManagerSpec.scala +++ b/core/baker-akka-actors/src/test/scala/com/ing/baker/runtime/recipe_manager/ActorBasedRecipeManagerSpec.scala @@ -3,7 +3,7 @@ package com.ing.baker.runtime.recipe_manager import _root_.akka.actor.ActorSystem import _root_.akka.testkit.{TestKit, TestProbe} import com.ing.baker.il.CompiledRecipe -import com.ing.baker.runtime.akka.AkkaBakerConfig.Timeouts +import com.ing.baker.runtime.akka.actor.Timeouts import com.ing.baker.runtime.akka.actor.recipe_manager.RecipeManagerProtocol._ import com.ing.baker.runtime.common.RecipeRecord import org.mockito.Mockito.when diff --git a/core/baker-akka-runtime/pom.xml b/core/baker-akka-runtime/pom.xml index 0f5a9668e..79e59defb 100644 --- a/core/baker-akka-runtime/pom.xml +++ b/core/baker-akka-runtime/pom.xml @@ -18,22 +18,22 @@ com.ing.baker - baker-types + baker-akka-actors ${project.version} com.ing.baker - baker-intermediate-language + baker-types ${project.version} com.ing.baker - baker-interface + baker-intermediate-language ${project.version} com.ing.baker - baker-recipe-dsl + baker-interface ${project.version} @@ -49,106 +49,6 @@ scala-library - - - com.typesafe.akka - akka-actor_${scala.compat.version} - - - com.typesafe.akka - akka-stream_${scala.compat.version} - - - com.typesafe.akka - akka-protobuf-v3_${scala.compat.version} - - - com.typesafe.akka - akka-persistence_${scala.compat.version} - - - com.typesafe.akka - akka-persistence-query_${scala.compat.version} - - - com.typesafe.akka - akka-cluster_${scala.compat.version} - - - com.typesafe.akka - akka-cluster-tools_${scala.compat.version} - - - com.typesafe.akka - akka-cluster-sharding_${scala.compat.version} - - - com.lightbend.akka.management - akka-management-cluster-bootstrap_${scala.compat.version} - - - com.typesafe.akka - akka-discovery_${scala.compat.version} - - - com.github.dnvriend - akka-persistence-inmemory_${scala.compat.version} - - - com.typesafe.akka - akka-actor-typed_${scala.compat.version} - - - com.typesafe.akka - akka-cluster-typed_${scala.compat.version} - - - com.typesafe.akka - akka-persistence-typed_${scala.compat.version} - - - com.typesafe.akka - akka-stream-typed_${scala.compat.version} - - - com.typesafe.akka - akka-slf4j_${scala.compat.version} - - - - - com.iheart - ficus_${scala.compat.version} - - - org.typelevel - cats-core_${scala.compat.version} - - - org.typelevel - cats-effect_${scala.compat.version} - - - com.thesamet.scalapb - scalapb-runtime_${scala.compat.version} - - - com.google.protobuf - protobuf-java - - - org.slf4j - slf4j-api - - - com.typesafe.scala-logging - scala-logging_${scala.compat.version} - - - nl.pragmasoft.sensors - sensors-core_${scala.compat.version} - - com.ing.baker @@ -168,16 +68,6 @@ akka-testkit_${scala.compat.version} test - - com.typesafe.akka - akka-multi-node-testkit_${scala.compat.version} - test - - - com.typesafe.akka - akka-stream-testkit_${scala.compat.version} - test - org.iq80.leveldb leveldb @@ -203,21 +93,11 @@ junit test - - ch.qos.logback - logback-classic - test - org.scalatest scalatest_${scala.compat.version} test - - org.scalacheck - scalacheck_${scala.compat.version} - test - org.scalatestplus scalacheck-1-17_${scala.compat.version} @@ -228,13 +108,6 @@ mockito-3-12_${scala.compat.version} test - - - - com.google.code.findbugs - jsr305 - provided - @@ -244,53 +117,11 @@ scala-maven-plugin - - org.xolstice.maven.plugins - protobuf-maven-plugin - - ${basedir}/src/main/protobuf - ${basedir}/target/generated-sources/protobuf/scala - false - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - - - scalapb - com.thesamet.scalapb - compilerplugin_${scala.compat.version} - ${scalapb.version} - scalapb.ScalaPbCodeGenerator - - - - - - - compile - - - - - org.scalatest scalatest-maven-plugin - - - org.apache.maven.plugins - maven-jar-plugin - ${maven.jar.plugin.version} - - - create-test-jar - package - - test-jar - - - - diff --git a/core/baker-akka-runtime/src/test/resources/application.conf b/core/baker-akka-runtime/src/main/resources/application.conf similarity index 100% rename from core/baker-akka-runtime/src/test/resources/application.conf rename to core/baker-akka-runtime/src/main/resources/application.conf diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBakerConfig.scala b/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBakerConfig.scala index df4598717..b23bc1762 100644 --- a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBakerConfig.scala +++ b/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBakerConfig.scala @@ -4,7 +4,7 @@ import akka.actor.{ActorSystem, Address, AddressFromURIString} import cats.data.NonEmptyList import cats.effect.IO import com.ing.baker.runtime.akka.AkkaBakerConfig.BakerValidationSettings -import com.ing.baker.runtime.akka.actor.{BakerActorProvider, ClusterBakerActorProvider, LocalBakerActorProvider} +import com.ing.baker.runtime.akka.actor.{BakerActorProvider, ClusterBakerActorProvider, LocalBakerActorProvider, Timeouts} import com.ing.baker.runtime.akka.internal.CachingInteractionManager import com.ing.baker.runtime.model.InteractionManager import com.ing.baker.runtime.recipe_manager.{ActorBasedRecipeManager, DefaultRecipeManager, RecipeManager} @@ -19,7 +19,7 @@ case class AkkaBakerConfig( bakerActorProvider: BakerActorProvider, interactions: InteractionManager[IO], recipeManager: RecipeManager, - timeouts: AkkaBakerConfig.Timeouts, + timeouts: Timeouts, bakerValidationSettings: BakerValidationSettings, terminateActorSystem: Boolean = true, )(implicit val system: ActorSystem) @@ -37,36 +37,6 @@ object AkkaBakerConfig extends LazyLogging { BakerValidationSettings(config.getOrElse[Boolean]("baker.allow-adding-recipe-without-requiring-instances", false)) } - case class Timeouts(defaultBakeTimeout: FiniteDuration, - defaultProcessEventTimeout: FiniteDuration, - defaultInquireTimeout: FiniteDuration, - defaultShutdownTimeout: FiniteDuration, - defaultAddRecipeTimeout: FiniteDuration, - defaultExecuteSingleInteractionTimeout: FiniteDuration) - - object Timeouts { - - def default: Timeouts = - Timeouts( - defaultBakeTimeout = 10.seconds, - defaultProcessEventTimeout = 10.seconds, - defaultInquireTimeout = 10.seconds, - defaultShutdownTimeout = 30.seconds, - defaultAddRecipeTimeout = 10.seconds, - defaultExecuteSingleInteractionTimeout = 60.seconds, - ) - - def apply(config: Config): Timeouts = - Timeouts( - defaultBakeTimeout = config.as[FiniteDuration]("baker.bake-timeout"), - defaultProcessEventTimeout = config.as[FiniteDuration]("baker.process-event-timeout"), - defaultInquireTimeout = config.as[FiniteDuration]("baker.process-inquire-timeout"), - defaultShutdownTimeout = config.as[FiniteDuration]("baker.shutdown-timeout"), - defaultAddRecipeTimeout = config.as[FiniteDuration]("baker.add-recipe-timeout"), - defaultExecuteSingleInteractionTimeout = config.as[FiniteDuration]("baker.execute-single-interaction-timeout"), - ) - } - def localDefault(actorSystem: ActorSystem): AkkaBakerConfig = { localDefault(actorSystem, CachingInteractionManager()) } @@ -81,7 +51,6 @@ object AkkaBakerConfig extends LazyLogging { providedIngredientFilter = List.empty, actorIdleTimeout = Some(5.minutes), configuredEncryption = Encryption.NoEncryption, - timeouts = defaultTimeouts, blacklistedProcesses = List.empty, rememberProcessDuration = None ) @@ -122,7 +91,6 @@ object AkkaBakerConfig extends LazyLogging { providedIngredientFilter = config.as[List[String]]("baker.filtered-ingredient-values") ++ config.as[List[String]]("baker.filtered-ingredient-values-for-stream"), actorIdleTimeout = config.as[Option[FiniteDuration]]("baker.actor.idle-timeout"), configuredEncryption = encryption, - Timeouts.apply(config), blacklistedProcesses = config.as[List[String]]("baker.blacklisted-processes"), rememberProcessDuration = config.as[Option[FiniteDuration]]("baker.process-index.remember-process-duration") ) @@ -142,7 +110,6 @@ object AkkaBakerConfig extends LazyLogging { getIngredientsFilter = config.as[List[String]]("baker.filtered-ingredient-values") ++ config.as[List[String]]("baker.filtered-ingredient-values-for-get"), providedIngredientFilter = config.as[List[String]]("baker.filtered-ingredient-values") ++ config.as[List[String]]("baker.filtered-ingredient-values-for-stream"), configuredEncryption = encryption, - Timeouts.apply(config), blacklistedProcesses = config.as[List[String]]("baker.blacklisted-processes"), rememberProcessDuration = config.as[Option[FiniteDuration]]("baker.process-index.remember-process-duration") ) diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java index 2cd737a1f..5c8362474 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/BakerTest.java @@ -30,7 +30,7 @@ public class BakerTest { private InteractionInstance interactionOneInstance = InteractionInstance.from(new JavaCompiledRecipeTest.InteractionOneImpl()); - private java.util.List implementationsList = ImmutableList.of( + private List implementationsList = ImmutableList.of( interactionOneInstance, InteractionInstance.from(new JavaCompiledRecipeTest.InteractionTwo()), InteractionInstance.from(new JavaCompiledRecipeTest.InteractionThreeImpl()), @@ -61,7 +61,7 @@ public void shouldSetupJBakerWithDefaultActorFramework() throws BakerException, String recipeInstanceId = UUID.randomUUID().toString(); Baker jBaker = AkkaBaker.java(config, actorSystem, implementationsList); - java.util.Map ingredients = jBaker.addRecipe(RecipeRecord.of(compiledRecipe, System.currentTimeMillis(), false, true)) + Map ingredients = jBaker.addRecipe(RecipeRecord.of(compiledRecipe, System.currentTimeMillis(), false, true)) .thenCompose(recipeId -> { assertEquals(compiledRecipe.getValidationErrors().size(), 0); return jBaker.bake(recipeId, recipeInstanceId); @@ -89,7 +89,7 @@ public void shouldSetupJBakerWithGivenActorFramework() throws BakerException, Ex String requestId = UUID.randomUUID().toString(); jBaker.bake(recipeId, requestId).get(); jBaker.fireEventAndResolveWhenCompleted(requestId, EventInstance.from(new JavaCompiledRecipeTest.EventOne())).get(); - java.util.Map ingredients = jBaker.getRecipeInstanceState(requestId).get().getIngredients(); + Map ingredients = jBaker.getRecipeInstanceState(requestId).get().getIngredients(); assertEquals(1, ingredients.size()); diff --git a/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java b/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java index 3b0825514..4ce374a41 100644 --- a/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java +++ b/core/baker-akka-runtime/src/test/java/com/ing/baker/compiler/JavaCompiledRecipeTest.java @@ -183,7 +183,7 @@ public interface InteractionThree { class Nothing {} - @FiresEvent(oneOf = {InteractionThree.Nothing.class}) + @FiresEvent(oneOf = {Nothing.class}) Nothing apply(@RequiresIngredient("RequestIDStringOne") String requestIDStringOne, @RequiresIngredient("RequestIDStringTwo") String requestIDStringTwo); } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/AllTypeRecipe.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/AllTypeRecipe.scala index 32d0db47c..7a3c7da8a 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/AllTypeRecipe.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/AllTypeRecipe.scala @@ -5,7 +5,6 @@ import com.ing.baker.recipe.common.InteractionFailureStrategy.RetryWithIncrement import com.ing.baker.recipe.scaladsl._ import org.joda.time.{DateTime, LocalDate, LocalDateTime} -import scala.collection.immutable.Seq import scala.concurrent.duration.DurationInt /** This recipe is meant for testing purposes. diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala index b6ea7e6cc..0e411b247 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ExamplesSpec.scala @@ -1,21 +1,19 @@ package com.ing.baker.runtime -import cats.effect.IO import com.ing.baker.BakerRuntimeTestBase import com.ing.baker.compiler.RecipeCompiler import com.ing.baker.recipe.scaladsl._ import com.ing.baker.runtime.ScalaDSLRuntime._ -import com.ing.baker.runtime.scaladsl.Baker -import com.typesafe.config.ConfigFactory - -import java.util.UUID import com.ing.baker.runtime.akka.AkkaBaker import com.ing.baker.runtime.akka.internal.CachingInteractionManager import com.ing.baker.runtime.common.RecipeRecord +import com.typesafe.config.ConfigFactory -import scala.concurrent.{ExecutionContext, Future} +import java.util.UUID +import scala.concurrent.Future -class ExamplesSpec extends BakerRuntimeTestBase { +class +ExamplesSpec extends BakerRuntimeTestBase { override def actorSystemName = "ExamplesSpec" "The WebShop recipe" should { diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ScalaDSLRuntime.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ScalaDSLRuntime.scala index dfa49831e..2099d53e3 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ScalaDSLRuntime.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/ScalaDSLRuntime.scala @@ -3,7 +3,7 @@ package com.ing.baker.runtime import com.ing.baker.recipe.scaladsl.{Event, Ingredient, Interaction} import com.ing.baker.runtime.scaladsl.{EventInstance, IngredientInstance, InteractionInstance, InteractionInstanceInput} import com.ing.baker.types.Converters.toJava -import com.ing.baker.types.{Converters, Type, Value} +import com.ing.baker.types.{Converters, Value} import scala.concurrent.Future import scala.reflect.runtime.universe.TypeTag diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerEventsSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerEventsSpec.scala index 5b986701c..05339f5b8 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerEventsSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerEventsSpec.scala @@ -8,8 +8,7 @@ import com.ing.baker.recipe.TestRecipe._ import com.ing.baker.recipe.common.InteractionFailureStrategy import com.ing.baker.recipe.scaladsl.{CheckPointEvent, Event, Recipe} import com.ing.baker.runtime.common.RejectReason._ -import com.ing.baker.runtime.scaladsl.{EventInstance, _} -import com.ing.baker.types.PrimitiveValue +import com.ing.baker.runtime.scaladsl._ import com.typesafe.scalalogging.LazyLogging import java.util.UUID diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala index 257833393..3f5c52acf 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerExecutionSpec.scala @@ -1,8 +1,5 @@ package com.ing.baker.runtime.akka -import java.net.MalformedURLException -import java.util.concurrent.TimeUnit -import java.util.{Optional, UUID} import akka.actor.ActorSystem import akka.persistence.inmemory.extension.{InMemoryJournalStorage, StorageExtension} import akka.testkit.{TestDuration, TestKit, TestProbe} @@ -11,7 +8,7 @@ import com.ing.baker.compiler.RecipeCompiler import com.ing.baker.recipe.TestRecipe._ import com.ing.baker.recipe.common.InteractionFailureStrategy import com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndBlock -import com.ing.baker.recipe.scaladsl.{CheckPointEvent, Event, Ingredient, Interaction, Recipe} +import com.ing.baker.recipe.scaladsl._ import com.ing.baker.runtime.akka.internal.CachingInteractionManager import com.ing.baker.runtime.common.BakerException._ import com.ing.baker.runtime.common.RecipeInstanceState.RecipeInstanceMetadataName @@ -20,10 +17,13 @@ import com.ing.baker.runtime.scaladsl.{Baker, EventInstance, InteractionInstance import com.ing.baker.types.{CharArray, Int32, PrimitiveValue, Value} import com.typesafe.config.{Config, ConfigFactory} import io.prometheus.client.CollectorRegistry -import org.mockito.ArgumentMatchers.{any, anyMap, anyString, argThat, eq => mockitoEq} +import org.mockito.ArgumentMatchers.{any, anyString, eq => mockitoEq} import org.mockito.Mockito._ import org.mockito.invocation.InvocationOnMock +import java.net.MalformedURLException +import java.util.concurrent.TimeUnit +import java.util.{Optional, UUID} import scala.concurrent.Future import scala.concurrent.duration._ import scala.language.postfixOps diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala index cdec5bfa1..1cbc42d99 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/recipe_manager/RecipeManagerActorSpec.scala @@ -1,7 +1,6 @@ package com.ing.baker.runtime.akka.actor.recipe_manager import java.util.UUID - import akka.actor.ActorRef import akka.pattern.ask import com.ing.baker.BakerRuntimeTestBase diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala index f6a722372..ba8ed2fb3 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/actor/serialization/SerializationSpec.scala @@ -20,9 +20,9 @@ import com.ing.baker.runtime.common.SensoryEventStatus import com.ing.baker.runtime.scaladsl.{EventInstance, EventMoment, RecipeInstanceState, SensoryEventResult} import com.ing.baker.runtime.serialization.Encryption.{AESEncryption, NoEncryption} import com.ing.baker.runtime.serialization.ProtoMap.{ctxFromProto, ctxToProto} +import com.ing.baker.{AllTypeRecipe, types} import com.ing.baker.types._ import com.ing.baker.types.modules.PrimitiveModuleSpec._ -import com.ing.baker.{AllTypeRecipe, types} import org.scalacheck.Prop.forAll import org.scalacheck.Test.Parameters.defaultVerbose import org.scalacheck._ diff --git a/core/baker-recipe-manager/pom.xml b/core/baker-recipe-manager/pom.xml new file mode 100644 index 000000000..ac10b1776 --- /dev/null +++ b/core/baker-recipe-manager/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + + com.ing.baker + baker + 5.1.0-SNAPSHOT + ../../pom.xml + + + baker-recipe-manager + Baker Recipe Managere + Recipe Manager for Baker + + + + com.ing.baker + baker-interface + ${project.version} + + + + + org.scalatest + scalatest_${scala.compat.version} + test + + + org.scalatestplus + mockito-3-12_${scala.compat.version} + test + + + + + + + net.alchim31.maven + scala-maven-plugin + + + + org.scalatest + scalatest-maven-plugin + + + + diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManager.scala b/core/baker-recipe-manager/src/main/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManager.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManager.scala rename to core/baker-recipe-manager/src/main/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManager.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/PollingAware.scala b/core/baker-recipe-manager/src/main/scala/com/ing/baker/runtime/recipe_manager/PollingAware.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/PollingAware.scala rename to core/baker-recipe-manager/src/main/scala/com/ing/baker/runtime/recipe_manager/PollingAware.scala diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/RecipeManager.scala b/core/baker-recipe-manager/src/main/scala/com/ing/baker/runtime/recipe_manager/RecipeManager.scala similarity index 100% rename from core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/recipe_manager/RecipeManager.scala rename to core/baker-recipe-manager/src/main/scala/com/ing/baker/runtime/recipe_manager/RecipeManager.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManagerSpec.scala b/core/baker-recipe-manager/src/test/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManagerSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManagerSpec.scala rename to core/baker-recipe-manager/src/test/scala/com/ing/baker/runtime/recipe_manager/DefaultRecipeManagerSpec.scala diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/recipe_manager/PollingAwareSpec.scala b/core/baker-recipe-manager/src/test/scala/com/ing/baker/runtime/recipe_manager/PollingAwareSpec.scala similarity index 100% rename from core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/recipe_manager/PollingAwareSpec.scala rename to core/baker-recipe-manager/src/test/scala/com/ing/baker/runtime/recipe_manager/PollingAwareSpec.scala diff --git a/pom.xml b/pom.xml index 9749008b7..fcc8ecd2c 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,9 @@ core/baker-recipe-dsl core/baker-recipe-dsl-kotlin core/baker-compiler + core/baker-akka-actors core/baker-akka-runtime + core/baker-recipe-manager core/baker-test From ebefefc69072f8a928d907b586854ddd282fc42f Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Tue, 26 May 2026 08:42:16 +0200 Subject: [PATCH 12/16] separate baker-compiler-kotlin maven module --- core/baker-compiler-kotlin/pom.xml | 163 +++ .../baker/compiler/PreCompileValidations.kt | 0 .../baker/compiler/RecipeCompilerKotlin.kt | 968 ++++++++++++++++++ .../compiler/RecipeValidationException.kt | 0 .../ing/baker/compiler/ScalaConversions.kt | 0 .../com/ing/baker/recipe/CheckPointEvent.kt | 0 .../main/kotlin/com/ing/baker/recipe/Event.kt | 0 .../baker/recipe/EventOutputTransformer.kt | 0 .../kotlin/com/ing/baker/recipe/Ingredient.kt | 0 .../ing/baker/recipe/IngredientDescriptor.kt | 0 .../com/ing/baker/recipe/Interaction.kt | 0 .../ing/baker/recipe/InteractionDescriptor.kt | 0 .../com/ing/baker/recipe/KotlinBridge.kt | 0 .../kotlin/com/ing/baker/recipe/Recipe.kt | 0 .../main/kotlin/com/ing/baker/recipe/Sieve.kt | 0 .../baker/compiler/RecipeCompilerBasicTest.kt | 10 +- .../baker/compiler/RecipeCompilerDslTest.kt | 4 +- .../compiler/RecipeCompilerFeaturesTest.kt | 88 +- .../compiler/RecipeCompilerIntegrationTest.kt | 18 +- .../baker/compiler/RecipeCompilerUnitTest.kt | 81 +- .../ing/baker/compiler/TestDataBuilders.kt | 2 +- core/baker-compiler/pom.xml | 82 +- .../com/ing/baker/compiler/RecipeCompiler.kt | 952 +---------------- pom.xml | 1 + 24 files changed, 1262 insertions(+), 1107 deletions(-) create mode 100644 core/baker-compiler-kotlin/pom.xml rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt (100%) create mode 100644 core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/Event.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/Interaction.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/Recipe.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/main/kotlin/com/ing/baker/recipe/Sieve.kt (100%) rename core/{baker-compiler => baker-compiler-kotlin}/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt (94%) rename core/{baker-compiler => baker-compiler-kotlin}/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt (91%) rename core/{baker-compiler => baker-compiler-kotlin}/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt (91%) rename core/{baker-compiler => baker-compiler-kotlin}/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt (93%) rename core/{baker-compiler => baker-compiler-kotlin}/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt (88%) rename core/{baker-compiler => baker-compiler-kotlin}/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt (99%) diff --git a/core/baker-compiler-kotlin/pom.xml b/core/baker-compiler-kotlin/pom.xml new file mode 100644 index 000000000..1e9aa854f --- /dev/null +++ b/core/baker-compiler-kotlin/pom.xml @@ -0,0 +1,163 @@ + + + 4.0.0 + + + com.ing.baker + baker + 5.1.0-SNAPSHOT + ../../pom.xml + + + baker-compiler-kotlin + Baker Recipe Compiler + Compiler for Baker recipes + + + + com.ing.baker + baker-recipe-dsl + ${project.version} + + + com.ing.baker + baker-recipe-dsl-kotlin + ${project.version} + + + com.ing.baker + baker-intermediate-language + ${project.version} + + + + + com.ing.baker + baker-recipe-dsl + ${project.version} + test-jar + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin.version} + test + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${build.helper.maven.plugin.version} + + + add-source + generate-sources + + add-source + + + + src/main/kotlin + + + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/kotlin + + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + ${jvm.target} + + + + compile + process-sources + + compile + + + + test-compile + + test-compile + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + create-test-jar + package + + test-jar + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + false + false + false + plain + + false + + + **/*Test.class + **/*Tests.class + + + **/*Spec.class + + + + + test + + test + + + + + + + diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt diff --git a/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt new file mode 100644 index 000000000..7832aa483 --- /dev/null +++ b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt @@ -0,0 +1,968 @@ +package com.ing.baker.compiler + +import com.ing.baker.compiler.PreCompileValidations.preCompileAssertions +import com.ing.baker.compiler.ScalaConversions.asJava +import com.ing.baker.compiler.ScalaConversions.asScala +import com.ing.baker.il.CompiledRecipe +import com.ing.baker.il.EventDescriptor +import com.ing.baker.il.IngredientDescriptor +import com.ing.baker.il.RecipeValidations +import com.ing.baker.il.ValidationSettings +import com.ing.baker.il.failurestrategy.`BlockInteraction$` +import com.ing.baker.il.failurestrategy.FireEventAfterFailure +import com.ing.baker.il.failurestrategy.FireFunctionalEventAfterFailure +import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff +import com.ing.baker.il.petrinet.Edge +import com.ing.baker.il.petrinet.EventTransition +import com.ing.baker.il.petrinet.InteractionTransition +import com.ing.baker.il.petrinet.IntermediateTransition +import com.ing.baker.il.petrinet.MissingEventTransition +import com.ing.baker.il.petrinet.MultiFacilitatorTransition +import com.ing.baker.il.petrinet.Place +import com.ing.baker.il.petrinet.Place.FiringLimiterPlace +import com.ing.baker.il.petrinet.Transition +import com.ing.baker.petrinet.api.PetriNet +import com.ing.baker.recipe.CheckPointEvent +import com.ing.baker.recipe.Event +import com.ing.baker.recipe.EventOutputTransformer +import com.ing.baker.recipe.Ingredient +import com.ing.baker.recipe.Interaction +import com.ing.baker.recipe.InteractionDescriptor +import com.ing.baker.recipe.Recipe +import com.ing.baker.recipe.Sieve +import com.ing.baker.recipe.common.InteractionFailureStrategy +import com.ing.baker.recipe.toKotlin +import com.ing.baker.types.`NullValue$` +import com.ing.baker.types.OptionType +import com.ing.baker.types.Type +import com.ing.baker.types.Value +import scala.Option +import scala.Some +import scala.collection.immutable.Seq +import scala.reflect.ClassTag +import scala.util.Either +import scala.util.Left +import scala.util.Right +import scalax.collection.Graph +import scalax.collection.`Graph$` +import scalax.collection.GraphEdge +import scalax.collection.config.CoreConfig +import scalax.collection.config.GraphConfig +import scalax.collection.edge.WLDiEdge +import scalax.collection.edge.`WLDiEdge$` +import scalax.collection.mutable.ArraySet +import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` as Scala212CompatibleScala +import com.ing.baker.il.failurestrategy.InteractionFailureStrategy as ILInteractionFailureStrategy +import com.ing.baker.il.`package$`.`MODULE$` as ILPackage +import com.ing.baker.il.petrinet.Place.`EmptyEventIngredientPlace$`.`MODULE$` as EmptyEventIngredientPlace +import com.ing.baker.il.petrinet.Place.`EventPreconditionPlace$`.`MODULE$` as EventPreconditionPlace +import com.ing.baker.il.petrinet.Place.`IngredientPlace$`.`MODULE$` as IngredientPlace +import com.ing.baker.il.petrinet.Place.`InteractionEventOutputPlace$`.`MODULE$` as InteractionEventOutputPlace +import com.ing.baker.il.petrinet.Place.`IntermediatePlace$`.`MODULE$` as IntermediatePlace +import com.ing.baker.il.petrinet.Place.`MultiTransitionPlace$`.`MODULE$` as MultiTransitionPlace +import com.ing.baker.recipe.common.Recipe as ScalaRecipe + +/** + * Type alias for the node type of the scalax.collection.Graph backing the petri net. + */ +typealias Node = Either + +/** + * Type alias for the edge type of the scalax.collection.Graph backing the petri net. + */ +typealias Arc = WLDiEdge + +/** + * Type alias for a multi set. + */ +typealias MultiSet = Map + +/** + * Type alias for a marking. + */ +typealias Marking = Map> + +/** + * Holds all components prepared from the recipe during the preparation phase. + */ +internal data class RecipeComponents( + val actionDescriptors: List, + val sensoryEvents: Set, + val allIngredientNames: Set +) + +/** + * Holds all transition types built during the transition building phase. + */ +internal data class TransitionCollections( + val allInteractionTransitions: List, + val sensoryEventTransitions: List, + val interactionEventTransitions: List, + val allEventTransitions: List, + val multipleOutputFacilitatorTransitions: List +) + +object RecipeCompilerKotlin { + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @return + */ + @JvmStatic + fun compileRecipe(recipe: ScalaRecipe): CompiledRecipe = + compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + @JvmStatic + fun compileRecipe( + recipe: ScalaRecipe, + validationSettings: ValidationSettings + ): CompiledRecipe = + compileRecipe(recipe.toKotlin(), validationSettings) + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + @JvmStatic + fun compileRecipe( + recipe: Recipe, + validationSettings: ValidationSettings + ): CompiledRecipe { + + val precompileErrors: List = preCompileAssertions(recipe) + + val (actionDescriptors, sensoryEvents, allIngredientNames) = prepareRecipeComponents(recipe) + + val transitions = buildAllTransitions( + actionDescriptors, + allIngredientNames, + sensoryEvents, + recipe.defaultFailureStrategy + ) + + val arcs = buildAllPetriNetArcs(actionDescriptors, transitions) + + val preconditionErrors = buildPreconditionErrors(transitions, actionDescriptors) + + return assemblePetriNetAndValidate(recipe, arcs, precompileErrors, preconditionErrors, validationSettings) + } + /** + * Gathers all components from the recipe including action descriptors, sensory events, and ingredient names. + */ + internal fun prepareRecipeComponents(recipe: Recipe): RecipeComponents { + // Extend the interactions with the checkpoint event interactions and sub-recipes + val actionDescriptors: List = recipe.interactions + + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + + recipe.sieves.map(::convertSieveToInteraction) + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + val sensoryEvents = flattenSensoryEvents(recipe) + + //All ingredient names provided by sensory events or by interactions + val allIngredientNames: Set = + sensoryEvents + .flatMap { e -> e.providedIngredients.map(Ingredient::name) } + .toSet() + + actionDescriptors.flatMap { i -> + i.output.flatMap { e -> + // check if the event was renamed (check if there is a transformer for this event) + when (val transformer = i.eventOutputTransformers[e]) { + null -> e.providedIngredients.map(Ingredient::name) + else -> e.providedIngredients.map { ingredient -> + transformer.ingredientRenames[ingredient.name] ?: ingredient.name + } + } + } + } + + return RecipeComponents(actionDescriptors, sensoryEvents, allIngredientNames) + } + + /** + * Creates interaction transitions, event transitions, and facilitator transitions. + */ + internal fun buildAllTransitions( + actionDescriptors: List, + allIngredientNames: Set, + sensoryEvents: Set, + failureStrategy: InteractionFailureStrategy, + ): TransitionCollections { + // For inputs for which no matching output cannot be found, we do not want to generate a place. + // It should be provided at runtime from outside the active petri net (marking) + val allInteractionTransitions: List = + actionDescriptors.map { interactionTransitionOf(it, failureStrategy, allIngredientNames) } + + // events provided from outside + val sensoryEventTransitions: List = sensoryEvents.map { event -> + EventTransition( + EventDescriptor( + event.name, + event.providedIngredients.map { IngredientDescriptor(it.name, it.type) }.asScala + ), true, event.maxFiringLimit.asScala + ) + } + + // events provided by other transitions / actions + val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire().asJava.map { event -> EventTransition(event, false, Option.empty()) } + } + + val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions + + val multipleOutputFacilitatorTransitions: List = + buildMultipleOutputFacilitatorTransitions(allInteractionTransitions) + + return TransitionCollections( + allInteractionTransitions, + sensoryEventTransitions, + interactionEventTransitions, + allEventTransitions, + multipleOutputFacilitatorTransitions + ) + } + + /** + * Builds internal event arcs that connect interaction transitions to ingredient places. + */ + internal fun buildInternalEventArcs( + allInteractionTransitions: List, + interactionEventTransitions: List + ): List { + // Given the event classes, it is creating the ingredient places and + // connecting a transition to a ingredient place. + val internalEventArcs: List = allInteractionTransitions.flatMap { t -> + t.eventsToFire.flatMap { event -> + event.ingredients.map { ingredient -> + val from = + interactionEventTransitions.find { it.label() == event.name() } + ?: throw RecipeValidationException() + arc(from, createPlace(ingredient.name(), IngredientPlace)) + } + } + } + return internalEventArcs + } + + /** + * Builds event limiter arcs for sensory events with firing limits. + */ + internal fun buildEventLimiterArcs(sensoryEventTransitions: List): List { + val eventLimiterArcs = sensoryEventTransitions.flatMap { t -> + when (val n = t.maxFiringLimit().getOrElse { null as Int? }) { + null -> emptyList() + else -> listOf(arc(createPlace("limit:${t.label()}", FiringLimiterPlace(n)), t)) + } + } + + return eventLimiterArcs + } + + /** + * Builds event precondition arcs (AND and OR) for all interactions. + */ + internal fun buildEventPreconditionArcs( + allEventTransitions: List, + allInteractionTransitions: List, + actionDescriptors: List + ): List { + fun findEventTransitionByEventName(eventName: String) = + allEventTransitions.find { it.event().name() == eventName } + + fun findInteractionByLabel(label: String) = + allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() + + // This generates precondition arcs for Required Events (AND). + val eventPreconditionArcs = actionDescriptors.flatMap { t -> + buildEventAndPreconditionArcs( + t, + ::findEventTransitionByEventName, + ::findInteractionByLabel + ) + } + + // This generates precondition arcs for Required Events (OR). + val eventOrPreconditionArcs = actionDescriptors.flatMap { t -> + buildEventORPreconditionArcs(t, ::findEventTransitionByEventName, ::findInteractionByLabel) + } + return eventPreconditionArcs + eventOrPreconditionArcs + } + + /** + * Builds sensory event arcs connecting sensory events to ingredient places or empty places. + */ + internal fun buildSensoryEventArcs( + sensoryEventTransitions: List, + actionDescriptors: List + ): List { + val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } + + // It connects a sensory event to an ingredient places + val sensoryEventArcs: List = sensoryEventWithIngredients.flatMap { et -> + et.event().ingredients.map { ingredient -> + arc(et, createPlace(ingredient.name(), IngredientPlace)) + } + } + + val eventThatArePreconditions: List = actionDescriptors.flatMap { actionDescriptor -> + actionDescriptor.requiredEvents + actionDescriptor.requiredOneOfEvents.flatten() + } + + // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net + val sensoryEventArcsNoIngredientsArcs: List = sensoryEventWithoutIngredients + //Filter out events that are preconditions to interactions + .filterNot { sensoryEvent -> eventThatArePreconditions.contains(sensoryEvent.label()) } + .map { sensoryEvent -> + arc(sensoryEvent, createPlace(sensoryEvent.label(), EmptyEventIngredientPlace)) + } + return sensoryEventArcs + sensoryEventArcsNoIngredientsArcs + } + + + /** + * Builds facilitator transitions for ingredients with multiple consumers. + */ + internal fun buildMultipleOutputFacilitatorTransitions(allInteractionTransitions: List): List { + // First find the cases where multiple transitions depend on the same ingredient place + val ingredientsWithMultipleConsumers: Map> = + getIngredientsWithMultipleConsumers(allInteractionTransitions) + + // Add one new transition for each duplicate input (the newly added one in the image above) + val multipleOutputFacilitatorTransitions: List = + ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) + return multipleOutputFacilitatorTransitions + } + + /** + * Builds facilitator arcs for ingredients with multiple consumers. + */ + internal fun buildMultipleOutputFacilitatorArcs(multipleOutputFacilitatorTransitions: List): List = + multipleOutputFacilitatorTransitions.map { t -> + arc(createPlace(t.label(), IngredientPlace), t) + } + + /** + * Builds interaction arcs for all interactions in the recipe. + */ + internal fun buildInteractionArcs( + allInteractionTransitions: List, + multipleOutputFacilitatorTransitions: List, + interactionEventTransitions: List + ): List { + val ingredientsWithMultipleConsumers = getIngredientsWithMultipleConsumers(allInteractionTransitions) + + return allInteractionTransitions.flatMap { interactionTransition -> + buildInteractionArcs( + multipleOutputFacilitatorTransitions, + ingredientsWithMultipleConsumers, + interactionEventTransitions, + interactionTransition + ) + } + } + + /** + * Consolidates all arc building logic. Arc order is preserved for backward compatibility. + */ + internal fun buildAllPetriNetArcs( + actionDescriptors: List, + transitions: TransitionCollections, + ): List { + val (allInteractionTransitions, sensoryEventTransitions, interactionEventTransitions, allEventTransitions, multipleOutputFacilitatorTransitions) = transitions + + val interactionArcs = buildInteractionArcs(allInteractionTransitions, multipleOutputFacilitatorTransitions, interactionEventTransitions) + + val eventPreconditionArcs = buildEventPreconditionArcs(allEventTransitions, allInteractionTransitions, actionDescriptors) + + val eventLimiterArcs: List = buildEventLimiterArcs(sensoryEventTransitions) + + val sensoryEventArcs = buildSensoryEventArcs(sensoryEventTransitions, actionDescriptors) + + val internalEventArcs: List = buildInternalEventArcs(allInteractionTransitions, interactionEventTransitions) + + val multipleOutputFacilitatorArcs = buildMultipleOutputFacilitatorArcs(multipleOutputFacilitatorTransitions) + + val arcs = (interactionArcs + + eventPreconditionArcs + + eventLimiterArcs + + sensoryEventArcs + + internalEventArcs + + multipleOutputFacilitatorArcs) + return arcs + } + + /** + * Constructs the final Petri net from transitions, arcs, places, and initial marking. + */ + internal fun assemblePetriNetAndValidate( + recipe: Recipe, + arcs: List, + precompileErrors: List, + preconditionErrors: List, + validationSettings: ValidationSettings + ): CompiledRecipe { + val petriNet = PetriNet(graph(arcs)) + + val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> + when (val placeType = p.placeType()) { + is FiringLimiterPlace -> p to mapOf(null to placeType.maxLimit()) + else -> null + } + }.toMap() + + val errors = preconditionErrors + precompileErrors + + val compiledRecipe = CompiledRecipe.build( + recipe.name, + petriNet, + initialMarking.mapValues { it.value.mapValues { it.value as Any }.asScala }.asScala, + errors.asScala, + recipe.eventReceivePeriod.asScala, + recipe.retentionPeriod.asScala, + Scala212CompatibleScala, + ) + + return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) + } + + private fun transition(transition: Transition) = Right(transition) + private fun place(place: Place) = Left(place) + + private fun arc(t: Transition, p: Place): Arc = + wlDiEdge(transition(t), place(p), Edge(Option.empty())) + + private fun arc(p: Place, t: Transition, eventFilter: String? = null): Arc = + wlDiEdge(place(p), transition(t), Edge(Option.apply(eventFilter))) + + /** + * Creates a transition for a missing event in the recipe. + * Creates a missing event transition placeholder. + * Used when an interaction requires an event that doesn't exist in the recipe. + */ + internal fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) + + private fun buildEventAndPreconditionErrors( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = + // Find the event in available events + interaction.requiredEvents.flatMap { eventName -> + buildEventPreconditionErrors( + eventName, + preconditionTransition, + interactionTransition(interaction.name) + ) + } + + private fun buildEventORPreconditionErrors( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = interaction.requiredOneOfEvents.flatMapIndexed { index: Int, orGroup: Set -> + orGroup.flatMap { eventName -> + buildEventPreconditionErrors( + eventName, + preconditionTransition, + interactionTransition(interaction.name) + ) + } + } + + private fun buildEventPreconditionErrors( + eventName: String, + preconditionTransition: (String) -> Transition?, + interactionTransition: Transition + ): List { + + val eventTransition = preconditionTransition(eventName) + + val notProvidedError = when (eventTransition) { + null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") + else -> emptyList() + } + + return notProvidedError + } + + /** + * Builds a list of error messages for missing required events. + * Validates that all event preconditions (AND and OR) are satisfied. + * + * @param transitions All transitions in the recipe + * @param actionDescriptors All interaction descriptors in the recipe + * @return List of error messages for missing required events + */ + internal fun buildPreconditionErrors( + transitions: TransitionCollections, + actionDescriptors: List + ): List { + fun findEventTransitionByEventName(eventName: String) = + transitions.allEventTransitions.find { it.event().name() == eventName } + + fun findInteractionByLabel(label: String) = + transitions.allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() + + // This generates precondition errors for Required Events (AND). + val preconditionANDErrors = actionDescriptors.flatMap { t -> + buildEventAndPreconditionErrors(t, ::findEventTransitionByEventName, ::findInteractionByLabel) + } + + // This generates precondition errors for Required Events (OR). + val preconditionORErrors = actionDescriptors.flatMap { t -> + buildEventORPreconditionErrors(t, ::findEventTransitionByEventName, ::findInteractionByLabel) + } + return preconditionORErrors + preconditionANDErrors + } + + private fun buildEventAndPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = + // Find the event in available events + interaction.requiredEvents.flatMap { eventName -> + // a new `Place` generated for each AND events + val eventPreconditionPlace = + createPlace( + label = "$eventName-${interaction.name}", + placeType = EventPreconditionPlace + ) + buildEventPreconditionArcs( + eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name) + ) + } + + private fun buildEventORPreconditionArcs( + interaction: InteractionDescriptor, + preconditionTransition: (String) -> Transition?, + interactionTransition: (String) -> Transition + ) = interaction.requiredOneOfEvents.flatMapIndexed { index: Int, orGroup: Set -> + // only one `Place` for all the OR events + val eventPreconditionPlace = createPlace( + label = "${interaction.name}-or-$index", + placeType = EventPreconditionPlace + ) + orGroup.flatMap { eventName -> + buildEventPreconditionArcs( + eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name) + ) + } + } + + private fun buildEventPreconditionArcs( + eventName: String, + preconditionPlace: Place, + preconditionTransition: (String) -> Transition?, + interactionTransition: Transition + ): List { + + val eventTransition = preconditionTransition(eventName) + + val arcs = listOf( + arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace), + arc(preconditionPlace, interactionTransition) + ) + + return arcs + } + + // the (possible) event output arcs / places + private fun buildInteractionOutputArcs( + interaction: InteractionTransition, + eventTransitions: List + ) = + if (interaction.eventsToFire.isNotEmpty()) { + val resultPlace = + createPlace(label = interaction.label(), placeType = InteractionEventOutputPlace) + val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor -> + //Get the correct event transition + val eventTransition = eventTransitions.find { it.event().name() == event.name() } + ?: throw RecipeValidationException("eventTransition should be found") + //Decide if there are multiple interactions that fire this transition, + // if so create a event combiner place + // else link the transition to the event. + val eventTransitionCount = eventTransitions.count { e -> e.event().name() == event.name() } + if (eventTransitionCount > 1) { + //Create a new intermediate event place + val eventCombinerPlace: Place = + createPlace(label = event.name(), placeType = IntermediatePlace) + //Create a new intermediate event transition + val interactionToEventTransition: IntermediateTransition = + IntermediateTransition("${interaction.interactionName()}:${event.name()}") + //link the interaction output place to the intermediate transition + val interactionOutputPlaceToIntermediateTransition: Arc = + arc(resultPlace, interactionToEventTransition, event.name()) + //link the intermediate transition to the intermediate input place + val intermediateTransitionToEventCombinerPlace: Arc = + arc(interactionToEventTransition, eventCombinerPlace) + //Link the intermediate place to the event place + val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition) + listOf( + intermediateTransitionToEventCombinerPlace, + interactionOutputPlaceToIntermediateTransition, + eventCombinerPlaceToEventTransition + ) + } else { + listOf(arc(resultPlace, eventTransition, event.name())) + } + } + eventArcs + arc(interaction, resultPlace) + } else emptyList() + + /** + * Draws an arc from all required ingredients for an interaction + * If the ingredient has multiple consumers create a multi transition place and create both arcs for it + */ + private fun buildInteractionInputArcs( + t: InteractionTransition, + multipleOutputFacilitatorTransitions: List, + ingredientsWithMultipleConsumers: Map> + ): List { + + val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = + t.nonProvidedIngredients.map(IngredientDescriptor::name) + .partition(ingredientsWithMultipleConsumers::contains) + + // the extra arcs to model multiple output transitions from one place + val internalDataInputArcs = fieldNamesWithPrefixMulti.flatMap { fieldName -> + val multiTransitionPlace = + createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace) + listOf( + // one arc from multiplier place to the transition + arc(getMultiTransition(fieldName, multipleOutputFacilitatorTransitions), multiTransitionPlace), + // one arc from extra added place to transition + arc(multiTransitionPlace, t) + ) + } + + // the data input arcs / places + val dataInputArcs: List = fieldNamesWithoutPrefix.map { fieldName -> + arc(createPlace(fieldName, IngredientPlace), t) + } + + val dataOutputArcs: List = + if (t.isReprovider) + fieldNamesWithoutPrefix.map { fieldName -> + arc(t, createPlace(fieldName, IngredientPlace)) + } + fieldNamesWithPrefixMulti.map { fieldName -> + arc(t, createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace)) + } + else + emptyList() + + val limitInteractionCountArc: List = when (val maximumInteractionCount = t.maximumInteractionCount) { + null -> emptyList() + else -> listOf( + arc(createPlace("limit:${t.label()}", FiringLimiterPlace(maximumInteractionCount)), t) + ) + } + + return dataInputArcs + dataOutputArcs + internalDataInputArcs + limitInteractionCountArc + } + + private fun buildInteractionArcs( + multipleOutputFacilitatorTransitions: List, + placeNameWithDuplicateTransitions: Map>, + eventTransitions: List, interactionTransition: InteractionTransition + ) = + buildInteractionInputArcs( + interactionTransition, + multipleOutputFacilitatorTransitions, + placeNameWithDuplicateTransitions + ) + buildInteractionOutputArcs( + interactionTransition, + eventTransitions + ) + + /** + * Finds a multi-transition (facilitator transition) by its internal representation name. + */ + private fun getMultiTransition(internalRepresentationName: String, transitions: List) = + transitions.find { it.label().equals(internalRepresentationName) } + ?: throw NoSuchElementException("No multi transition found with name $internalRepresentationName") + + /** + * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. + * + * @param actionTransitions Seq of reflected transition. + * @return A map from input place name to reflected transitions (where the transitions have as input the place). + */ + private fun getIngredientsWithMultipleConsumers(actionTransitions: List): Map> = + // Obtain a list of field name with their transition + actionTransitions + .flatMap { transition -> + transition.nonProvidedIngredients.map { ingredient -> + ingredient.name() to transition + } + } + .groupBy({ it.first }, { it.second }) + // Only keep those place names which have more than one out-adjacent transition + .filter { (_, interactions) -> interactions.size >= 2 } + + /** + * Creates a Place with the given label and type. + * The label is prefixed with the place type's prefix. + * Creates a Place in the Petri net with the appropriate label prefix. + */ + internal fun createPlace(label: String, placeType: Place.PlaceType): Place = + Place("${placeType.labelPrepend()}$label", placeType) + + /** + * Converts a CheckPointEvent into an InteractionDescriptor. + * Checkpoint events are special events that can be used to track progress in a recipe. + * They are converted to interactions with no input ingredients. + */ + private fun convertCheckpointEventToInteraction(e: CheckPointEvent) = + Interaction( + name = "${ILPackage.checkpointEventInteractionPrefix()}${e.name}", + inputIngredients = emptyList(), + output = listOf(Event(e.name, emptyList())), + requiredEvents = e.requiredEvents, + requiredOneOfEvents = e.requiredOneOfEvents + ) + + /** + * Converts a Sieve into an InteractionDescriptor. + * Sieves are interactions that filter/transform ingredients without requiring specific events. + */ + private fun convertSieveToInteraction(s: Sieve) = + Interaction( + name = "${ILPackage.sieveInteractionPrefix()}${s.name}", + inputIngredients = s.inputIngredients, + output = s.output + ) + + /** + * Flattens sub-recipes into InteractionDescriptors. + * Each interaction in a sub-recipe is copied and prefixed to avoid name conflicts. + */ + private fun flattenSubRecipesToInteraction(recipe: Recipe): Set { + fun copyInteraction(i: InteractionDescriptor) = com.ing.baker.recipe.Interaction( + $$"$${ILPackage.subRecipePrefix()}$${recipe.name}$$${i.name}", + i.originalName, + i.inputIngredients, + i.output, + i.requiredEvents, + i.requiredOneOfEvents, + i.predefinedIngredients, + i.overriddenIngredientNames, + i.overriddenOutputIngredientName, + i.eventOutputTransformers, + i.maximumInteractionCount, + i.failureStrategy, + i.isReprovider, + ) + return recipe.interactions.map(::copyInteraction).toSet() + + recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + + recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + + recipe.sieves.map(::convertSieveToInteraction) + } + + /** + * Flattens sensory events from a recipe including events from sub-recipes recursively. + */ + private fun flattenSensoryEvents(recipe: Recipe): Set = + recipe.sensoryEvents + recipe.subRecipes.flatMap(::flattenSensoryEvents) + + /** + * Creates a Petri net graph from a list of arcs. + * Uses the Scala graph library to construct the graph structure. + */ + private fun graph(arcs: List): Graph> = + `Graph$`.`MODULE$`.from( + arcs.map { it as WLDiEdge }.asScala, + ClassTag.apply(WLDiEdge::class.java), + CoreConfig(GraphConfig.defaultOrder(), ArraySet.`Hints$`.`MODULE$`.apply(16, 32, 48, 80)) + ) as Graph> + + /** + * Creates a weighted labeled directed edge between two nodes. + * Used to create arcs in the Petri net graph. + */ + private fun wlDiEdge(node1: Node, node2: Node, label: L): WLDiEdge = + `WLDiEdge$`.`MODULE$`.newEdge(GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), 1.0, label) + + /** + * Converts an InteractionDescriptor to an InteractionTransition for use in the Petri net. + * + * This function transforms recipe-level interaction descriptors into IL-level transitions, + * handling event transformations, ingredient mapping, optional ingredients, and failure strategies. + */ + internal fun interactionTransitionOf( + interactionDescriptor: InteractionDescriptor, + defaultFailureStrategy: InteractionFailureStrategy, + allIngredientNames: Set + ): InteractionTransition { + //This transforms the event using the eventOutputTransformer to the new event + //If there is no eventOutputTransformer for the event the original event is returned + fun transformEventType(event: Event): Event = + when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers[event]) { + null -> event + else -> Event( + eventOutputTransformer.newEventName, + event.providedIngredients.map { i -> + Ingredient( + eventOutputTransformer.ingredientRenames.getOrElse(i.name, { i.name }), + i.type + ) + }, + null + ) + } + + fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = + com.ing.baker.il.EventOutputTransformer( + recipeEventOutputTransformer.newEventName, + recipeEventOutputTransformer.ingredientRenames.asScala + ) + + fun transformEventToCompiledEvent(event: Event): EventDescriptor = + EventDescriptor( + event.name, + event.providedIngredients.map { IngredientDescriptor(it.name, it.type) }.asScala + ) + + // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe + // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe + // Replace BakerEventList to BakerEventList tag as know in compiledRecipe + // Replace ingredient tags with overridden tags + val inputFields: Seq> = interactionDescriptor.inputIngredients + .map { ingredient -> + when (ingredient.name) { + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> ILPackage.recipeInstanceIdName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> ILPackage.recipeInstanceMetadataName() + com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> ILPackage.recipeInstanceEventListName() + else -> interactionDescriptor.overriddenIngredientNames + .getOrElse(ingredient.name, { ingredient.name }) + } to ingredient.type + }.asScala + + val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala + val eventsToFire = + interactionDescriptor.output.map(::transformEventType).map(::transformEventToCompiledEvent).asScala + + //For each ingredient that is not provided + //And is of the type Optional or Option + //Add it to the predefinedIngredients List as empty + //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. + val predefinedIngredientsWithOptionalsEmpty: Map = + inputFields.asJava + .filter { (name, type) -> type is OptionType && name !in allIngredientNames } + .associate { Pair(it.first, `NullValue$`.`MODULE$`) } + + interactionDescriptor.predefinedIngredients + + val p: Triple, Option> = + when (val strategy = interactionDescriptor.failureStrategy ?: defaultFailureStrategy) { + is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { + val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { + is Some -> Some( + EventDescriptor( + e.value().getOrElse { null as String? } + ?: (interactionDescriptor.name + ILPackage.exhaustedEventAppend()), + emptyList().asScala) + ) + + else -> Option.empty() + } + val functionalFailedEvent = when (val e = strategy.fireFunctionalEvent()) { + is Some -> Some( + EventDescriptor( + e.value().getOrElse { null as String? } + ?: (interactionDescriptor.name + ILPackage.functionalFailedEventAppend()), + emptyList().asScala) + ) + + else -> Option.empty() + } + Triple( + RetryWithIncrementalBackoff( + strategy.initialDelay(), + strategy.backoffFactor(), + strategy.maximumRetries(), + strategy.maxTimeBetweenRetries(), + exhaustedRetryEvent, + functionalFailedEvent, + ), exhaustedRetryEvent, functionalFailedEvent + ) + } + + is InteractionFailureStrategy.BlockInteraction -> Triple( + `BlockInteraction$`.`MODULE$`, + Option.empty(), + Option.empty() + ) + + is InteractionFailureStrategy.FireEventAfterFailure -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name + ILPackage.exhaustedEventAppend() } + val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) + Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) + } + + is InteractionFailureStrategy.FireEventAndBlock -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name + ILPackage.exhaustedEventAppend() } + val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) + Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) + } + + is InteractionFailureStrategy.FireEventAndResolve -> { + val eventName = strategy.eventName() + .getOrElse { interactionDescriptor.name + ILPackage.functionalFailedEventAppend() } + val functionalFailed = EventDescriptor(eventName, emptyList().asScala) + Triple(FireFunctionalEventAfterFailure(functionalFailed), Option.empty(), Some(functionalFailed)) + } + + else -> Triple(`BlockInteraction$`.`MODULE$`, Option.empty(), Option.empty()) + } + val failureStrategy = p.first + val exhaustedRetryEvent = p.second.toList().toSeq() + val functionalRetryEvent = p.third.toList().toSeq() + + val eventsToFireAll = eventsToFire.asJava + exhaustedRetryEvent.asJava + functionalRetryEvent.asJava + val originalEventsAll = originalEvents.asJava + exhaustedRetryEvent.asJava + functionalRetryEvent.asJava + + return InteractionTransition( + eventsToFireAll.asScala, + originalEventsAll.asScala, + inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala, + interactionDescriptor.name, + interactionDescriptor.originalName, + predefinedIngredientsWithOptionalsEmpty.asScala, + interactionDescriptor.maximumInteractionCount.asScala, + failureStrategy, + interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> + event.name to transformEventOutputTransformer( + transformer + ) + }.toMap().asScala, + interactionDescriptor.isReprovider + ) + } +} + +val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava +val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava +val InteractionTransition.maximumInteractionCount + get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } +val EventDescriptor.ingredients get() = this.ingredients().asJava + + diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeValidationException.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/ScalaConversions.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/CheckPointEvent.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Event.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Event.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Event.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Event.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/EventOutputTransformer.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Ingredient.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/IngredientDescriptor.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Interaction.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Interaction.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Interaction.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/InteractionDescriptor.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/KotlinBridge.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Recipe.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Recipe.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Recipe.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Recipe.kt diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Sieve.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Sieve.kt similarity index 100% rename from core/baker-compiler/src/main/kotlin/com/ing/baker/recipe/Sieve.kt rename to core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/recipe/Sieve.kt diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt similarity index 94% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt rename to core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt index ba19bd0a4..10c8cd898 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt +++ b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerBasicTest.kt @@ -58,7 +58,7 @@ class RecipeCompilerBasicTest { fun shouldCompileSimpleRecipe() { val recipe = Recipe("recipe") .withSensoryEvents(EventA::class.java) - val compiled = RecipeCompiler.compileRecipe(recipe) + val compiled = RecipeCompilerKotlin.compileRecipe(recipe) Assertions.assertEquals("ed72cc8637c9cd07", compiled.recipeId()) } @@ -68,7 +68,7 @@ class RecipeCompilerBasicTest { val recipe = Recipe("recipe") .withSensoryEvents(EventA::class.java) .withInteraction(InteractionDescriptor.of(InteractionA::class.java)) - val compiled = RecipeCompiler.compileRecipe(recipe) + val compiled = RecipeCompilerKotlin.compileRecipe(recipe) Assertions.assertEquals("9b2bc4caf5752697", compiled.recipeId()) } @@ -85,7 +85,7 @@ class RecipeCompilerBasicTest { .withSensoryEventsNoFiringLimit(EventA::class.java) Assertions.assertThrows(RecipeValidationException::class.java) { - RecipeCompiler.compileRecipe(recipe) + RecipeCompilerKotlin.compileRecipe(recipe) } } @@ -99,7 +99,7 @@ class RecipeCompilerBasicTest { .withRequiredEvents(EventB::class.java, EventC::class.java) ) - val compiled = RecipeCompiler.compileRecipe(recipe) + val compiled = RecipeCompilerKotlin.compileRecipe(recipe) val actual = convertList(compiled.petriNet().transitions()) .filterIsInstance() @@ -142,7 +142,7 @@ class RecipeCompilerBasicTest { .withSubRecipe(subRecipe) .withCheckpointEvent(CheckPointEvent("checkpointEvent")) - val compiled = RecipeCompiler.compileRecipe(recipe) + val compiled = RecipeCompilerKotlin.compileRecipe(recipe) val actualSensoryEvents = convertList(compiled.sensoryEvents()) .map { (it as com.ing.baker.il.EventDescriptor).name() } diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt similarity index 91% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt rename to core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt index d8ac80909..acfe259f2 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt +++ b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerDslTest.kt @@ -32,7 +32,7 @@ class RecipeCompilerDslTest { interaction() } - val compiled = RecipeCompiler.compileRecipe(recipe) + val compiled = RecipeCompilerKotlin.compileRecipe(recipe) assertEquals("9b2bc4caf5752697", compiled.recipeId()) } @@ -50,7 +50,7 @@ class RecipeCompilerDslTest { ingredient("extract") @JvmSerializableLambda { "Hello123" } } - val compiled = RecipeCompiler.compileRecipe(recipe) + val compiled = RecipeCompilerKotlin.compileRecipe(recipe) assertEquals("4e146307a299c515", compiled.recipeId()) } diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt similarity index 91% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt rename to core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt index 57076df61..b7b62c3bc 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt +++ b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerFeaturesTest.kt @@ -66,7 +66,7 @@ class RecipeCompilerFeaturesTest { @Test fun `The recipe compiler should not have validation errors for a valid recipe`() { val recipe: Recipe = getRecipe("ValidRecipe") - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) } @@ -79,7 +79,7 @@ class RecipeCompilerFeaturesTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAfterFailure(Some(exhaustedEvent().name())) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -92,7 +92,7 @@ class RecipeCompilerFeaturesTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAfterFailure(Option.empty()) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -105,7 +105,7 @@ class RecipeCompilerFeaturesTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndBlock(Some(exhaustedEvent().name())) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -118,7 +118,7 @@ class RecipeCompilerFeaturesTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndBlock(Option.empty()) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -131,7 +131,7 @@ class RecipeCompilerFeaturesTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndResolve(Some(exhaustedEvent().name())) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -144,7 +144,7 @@ class RecipeCompilerFeaturesTest { com.ing.baker.recipe.common.InteractionFailureStrategy.FireEventAndResolve(Option.empty()) ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("InteractionOneFunctionalFailed" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -162,7 +162,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -181,7 +181,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -200,7 +200,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -219,7 +219,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -238,7 +238,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -257,7 +257,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("InteractionOneRetryExhausted" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -276,7 +276,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -295,7 +295,7 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(exhaustedEvent().name() in compiledRecipe.allEvents.map(EventDescriptor::name)) } @@ -314,17 +314,17 @@ class RecipeCompilerFeaturesTest { ) ) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("InteractionOneFunctionalFailed" in compiledRecipe.allEvents.map(EventDescriptor::name)) } @Test fun `The recipe compiler should generate the same id for the same recipe`() { - val first = RecipeCompiler.compileRecipe(getRecipe("ValidRecipe")).recipeId() + val first = RecipeCompilerKotlin.compileRecipe(getRecipe("ValidRecipe")).recipeId() (1..10) .map { getRecipe("ValidRecipe") } - .map { RecipeCompiler.compileRecipe(it).recipeId() } + .map { RecipeCompilerKotlin.compileRecipe(it).recipeId() } .forEach { assertEquals(first, it) } } @@ -336,7 +336,7 @@ class RecipeCompilerFeaturesTest { val name = "RecipeName" val recipe1 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 1)) val recipe2 = recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name() to 2)) - assertTrue(RecipeCompiler.compileRecipe(recipe1).recipeId() != RecipeCompiler.compileRecipe(recipe2).recipeId()) + assertTrue(RecipeCompilerKotlin.compileRecipe(recipe1).recipeId() != RecipeCompilerKotlin.compileRecipe(recipe2).recipeId()) } @Test @@ -345,7 +345,7 @@ class RecipeCompilerFeaturesTest { .withSensoryEvent(secondEvent()) .withInteractions(interactionOne()) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction" in compiledRecipe.validationErrors) } @@ -362,7 +362,7 @@ class RecipeCompilerFeaturesTest { .withSensoryEvent(initialEvent()) .withInteractions(wrongrecipeInstanceIdInteraction) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("Non supported process id type: Int32 on interaction: 'wrongrecipeInstanceIdInteraction'" in compiledRecipe.validationErrors) } @@ -379,7 +379,7 @@ class RecipeCompilerFeaturesTest { .withSensoryEvent(initialEvent()) .withInteractions(wrongMetaDataInteraction) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("Non supported MetaData type: Int32 on interaction: 'wrongMetaDataInteraction'" in compiledRecipe.validationErrors) } @@ -411,7 +411,7 @@ class RecipeCompilerFeaturesTest { .withSensoryEvents(initialEvent(), wrongDateEvent) .withInteractions(wrongMetaDataInteraction) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertEquals( listOf( "Ingredient 'RecipeInstanceMetaData' is provided and this is a reserved name for internal use in Baker", @@ -439,7 +439,7 @@ class RecipeCompilerFeaturesTest { ) .withSensoryEvent(initialEventInt) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("Interaction 'InteractionOne' expects ingredient 'initialIngredient:CharArray', however incompatible type: 'Record(data: Int32)' was provided" in compiledRecipe.validationErrors) } @@ -464,7 +464,7 @@ class RecipeCompilerFeaturesTest { ) .withSensoryEvents(initialEventIntOptional, initialEventIntOption) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionalInt:Option[Int32]', however incompatible type: 'Option[CharArray]' was provided" in compiledRecipe.validationErrors) assertTrue("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionInt:Option[List[Int32]]', however incompatible type: 'Option[List[CharArray]]' was provided" in compiledRecipe.validationErrors) } @@ -486,7 +486,7 @@ class RecipeCompilerFeaturesTest { ) .withSensoryEvent(initialEventInt) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) } @@ -500,7 +500,7 @@ class RecipeCompilerFeaturesTest { ) .withSensoryEvent(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("Predefined argument 'initialIngredient' is not of type: CharArray on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) } @@ -513,7 +513,7 @@ class RecipeCompilerFeaturesTest { ) .withSensoryEvent(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue("Predefined argument 'WrongIngredient' is not defined on interaction: 'InteractionOne'" in compiledRecipe.validationErrors) } @@ -523,7 +523,7 @@ class RecipeCompilerFeaturesTest { .withInteractions(interactionSeven().withMaximumInteractionCount(1), interactionEight()) .withSensoryEvent(initialEvent()) - val compiledRecipe = RecipeCompiler.compileRecipe( + val compiledRecipe = RecipeCompilerKotlin.compileRecipe( recipe, ValidationSettings(true, true , false) ) @@ -539,7 +539,7 @@ class RecipeCompilerFeaturesTest { recipe("InteractionNameTest").withInteractions(invalidInteraction).withSensoryEvent(initialEvent()) assertFailsWith(exceptionMessage = "Interaction with a null or empty name found") { - RecipeCompiler.compileRecipe(recipe) + RecipeCompilerKotlin.compileRecipe(recipe) } } } @@ -551,7 +551,7 @@ class RecipeCompilerFeaturesTest { val recipe = recipe("EventNameTest").withSensoryEvent(invalidEvent).withInteraction(interactionOne()) assertFailsWith(exceptionMessage = "Event with a null or empty name found") { - RecipeCompiler.compileRecipe(recipe) + RecipeCompilerKotlin.compileRecipe(recipe) } } } @@ -564,7 +564,7 @@ class RecipeCompilerFeaturesTest { .withInteraction(interactionOne()) assertFailsWith(exceptionMessage = "Ingredient with a null or empty name found") { - RecipeCompiler.compileRecipe(recipe) + RecipeCompilerKotlin.compileRecipe(recipe) } } } @@ -575,7 +575,7 @@ class RecipeCompilerFeaturesTest { val recipe = recipe(name) assertFailsWith(exceptionMessage = "Recipe with a null or empty name found") { - RecipeCompiler.compileRecipe(recipe) + RecipeCompilerKotlin.compileRecipe(recipe) } } } @@ -586,7 +586,7 @@ class RecipeCompilerFeaturesTest { .withInteraction(interactionOne().isReprovider(true)) .withSensoryEvents(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertEquals( listOf("Reprovider interaction InteractionOne needs to have a event precondition"), compiledRecipe.validationErrors @@ -599,7 +599,7 @@ class RecipeCompilerFeaturesTest { .withInteraction(interactionWithOptionalIngredients()) .withSensoryEvent(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -621,7 +621,7 @@ class RecipeCompilerFeaturesTest { .withInteraction(interactionWithOptionalIngredients()) .withSensoryEvents(initialEvent(), optionalProviderEvent) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -658,7 +658,7 @@ class RecipeCompilerFeaturesTest { ) .withInteraction(secondInteraction) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) val transition = @@ -680,7 +680,7 @@ class RecipeCompilerFeaturesTest { ) .withSensoryEvents(initialEvent()) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -702,7 +702,7 @@ class RecipeCompilerFeaturesTest { .withInteraction(interactionWithOptionalIngredients()) .withSensoryEvents(initialEvent(), optionalProviderEvent) - val compiledRecipe: CompiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe: CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertTrue(compiledRecipe.validationErrors.isEmpty()) compiledRecipe.interactionTransitions().foreach { transition -> @@ -718,8 +718,8 @@ class RecipeCompilerFeaturesTest { @Test fun `The recipe compiler should give the correct id when it compiles a java recipe`() { val recipe = TestRecipeJava.getRecipe("id-test-recipe") - val compiledRecipeKotlin = RecipeCompiler.compileRecipe(recipe) - val compiledRecipeScala = RecipeCompiler.compileRecipe(recipe) + val compiledRecipeKotlin = RecipeCompilerKotlin.compileRecipe(recipe) + val compiledRecipeScala = RecipeCompilerKotlin.compileRecipe(recipe) assertEquals(compiledRecipeScala.recipeId(), compiledRecipeKotlin.recipeId()) assertEquals("9a7f1374762c9b0e", compiledRecipeKotlin.recipeId()) @@ -728,7 +728,7 @@ class RecipeCompilerFeaturesTest { @Test fun `The recipe compiler should give the interaction with Reprovider enabled when it compiles a java recipe and changes recipeId`() { val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertEquals("8a9d731ed4dae89e", compiledRecipe.recipeId()) } @@ -742,7 +742,7 @@ class RecipeCompilerFeaturesTest { emptySet>().asScala ).withRequiredEvent(initialEvent()) ) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertEquals("5c6aab1ddb03521f", compiledRecipe.recipeId()) assertEquals(1, compiledRecipe.petriNet().transitions().count { it is InteractionTransition && it.interactionName() @@ -794,7 +794,7 @@ class RecipeCompilerFeaturesTest { ) .withSubRecipe(subRecipe) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) val res = compiledRecipe.petriNet().transitions().asJava .flatMap { diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt similarity index 93% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt rename to core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt index 6ddedd9a0..137ff684a 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt +++ b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerIntegrationTest.kt @@ -6,11 +6,9 @@ import com.ing.baker.recipe.annotations.RequiresIngredient import com.ing.baker.recipe.javadsl.Interaction import com.ing.baker.recipe.javadsl.InteractionDescriptor import com.ing.baker.recipe.javadsl.Recipe -import com.ing.baker.types.Converters import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import scala.jdk.javaapi.CollectionConverters -import scala.jdk.javaapi.OptionConverters /** * Integration tests for the refactored RecipeCompiler. @@ -58,7 +56,7 @@ class RecipeCompilerIntegrationTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) // Verify that the recipe compiled successfully assertNotNull(compiledRecipe) @@ -80,7 +78,7 @@ class RecipeCompilerIntegrationTest { .withSensoryEvent(PaymentMade::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) val petriNet = compiledRecipe.petriNet() @@ -108,7 +106,7 @@ class RecipeCompilerIntegrationTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) val petriNet = compiledRecipe.petriNet() @@ -127,7 +125,7 @@ class RecipeCompilerIntegrationTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) // Verify basic properties assertNotNull(compiledRecipe) @@ -152,7 +150,7 @@ class RecipeCompilerIntegrationTest { .withRequiredEvents(OrderConfirmed::class.java) ) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) // Should compile despite missing OrderConfirmed event (validation will catch it) assertNotNull(compiledRecipe) @@ -170,7 +168,7 @@ class RecipeCompilerIntegrationTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) // Both interactions use orderId - should create facilitator transition val petriNet = compiledRecipe.petriNet() @@ -194,7 +192,7 @@ class RecipeCompilerIntegrationTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) // Verify structure assertNotNull(compiledRecipe) @@ -214,7 +212,7 @@ class RecipeCompilerIntegrationTest { fun `empty recipe should compile without errors`() { val recipe = Recipe("EmptyRecipe") - val compiledRecipe = RecipeCompiler.compileRecipe(recipe) + val compiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) assertNotNull(compiledRecipe) assertEquals("EmptyRecipe", compiledRecipe.name()) diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt similarity index 88% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt rename to core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt index 08b55cbf3..2bcafba03 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt +++ b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/RecipeCompilerUnitTest.kt @@ -1,6 +1,5 @@ package com.ing.baker.compiler -import com.ing.baker.compiler.RecipeCompiler.interactionTransitionOf import com.ing.baker.il.ValidationSettings import com.ing.baker.recipe.annotations.FiresEvent import com.ing.baker.recipe.annotations.RequiresIngredient @@ -59,7 +58,7 @@ class RecipeCompilerUnitTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) assertEquals(2, actionDescriptors.size, "Should extract 2 interactions") assertEquals(1, sensoryEvents.count(), "Should extract 1 sensory event") @@ -73,7 +72,7 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (_, _, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) + val (_, _, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) assertTrue(allIngredientNames.contains("orderId"), "Should include orderId from OrderPlaced") assertTrue(allIngredientNames.contains("items"), "Should include items from OrderPlaced") @@ -84,7 +83,7 @@ class RecipeCompilerUnitTest { fun `prepareRecipeComponents handles empty recipe`() { val recipe = Recipe("EmptyRecipe") - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) assertEquals(0, actionDescriptors.size) assertEquals(0, sensoryEvents.count()) @@ -97,8 +96,8 @@ class RecipeCompilerUnitTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, @@ -116,8 +115,8 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withSensoryEvent(PaymentMade::class.java) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, @@ -136,8 +135,8 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, @@ -156,8 +155,8 @@ class RecipeCompilerUnitTest { .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) .withInteraction(InteractionDescriptor.of(ProcessPayment::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, @@ -178,8 +177,8 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, @@ -196,14 +195,14 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, recipe.defaultFailureStrategy() ) - val arcs = RecipeCompiler.buildAllPetriNetArcs(actionDescriptors, transitions) + val arcs = RecipeCompilerKotlin.buildAllPetriNetArcs(actionDescriptors, transitions) assertTrue(arcs.isNotEmpty(), "Should create arcs") // Arcs should include: interaction arcs, sensory event arcs, internal event arcs, etc. @@ -215,16 +214,16 @@ class RecipeCompilerUnitTest { .withSensoryEvent(OrderPlaced::class.java) .withInteraction(InteractionDescriptor.of(ReserveItems::class.java)) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, recipe.defaultFailureStrategy() ) - val arcs = RecipeCompiler.buildAllPetriNetArcs(actionDescriptors, transitions) + val arcs = RecipeCompilerKotlin.buildAllPetriNetArcs(actionDescriptors, transitions) - val compiledRecipe = RecipeCompiler.assemblePetriNetAndValidate( + val compiledRecipe = RecipeCompilerKotlin.assemblePetriNetAndValidate( recipe.toKotlin(), arcs, emptyList(), @@ -243,18 +242,18 @@ class RecipeCompilerUnitTest { val recipe = Recipe("TestRecipe") .withSensoryEvent(OrderPlaced::class.java) - val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompiler.prepareRecipeComponents(recipe.toKotlin()) - val transitions = RecipeCompiler.buildAllTransitions( + val (actionDescriptors, sensoryEvents, allIngredientNames) = RecipeCompilerKotlin.prepareRecipeComponents(recipe.toKotlin()) + val transitions = RecipeCompilerKotlin.buildAllTransitions( actionDescriptors, allIngredientNames, sensoryEvents, recipe.defaultFailureStrategy() ) - val arcs = RecipeCompiler.buildAllPetriNetArcs(actionDescriptors, transitions) + val arcs = RecipeCompilerKotlin.buildAllPetriNetArcs(actionDescriptors, transitions) val precompileErrors = listOf("Test error 1") val preconditionErrors = listOf("Test error 2") - val compiledRecipe = RecipeCompiler.assemblePetriNetAndValidate( + val compiledRecipe = RecipeCompilerKotlin.assemblePetriNetAndValidate( recipe.toKotlin(), arcs, precompileErrors, @@ -284,7 +283,7 @@ class RecipeCompilerUnitTest { ingredientNames = listOf("reservedItems") ) - val arcs = RecipeCompiler.buildInternalEventArcs( + val arcs = RecipeCompilerKotlin.buildInternalEventArcs( listOf(interactionTransition), listOf(eventTransition) ) @@ -309,7 +308,7 @@ class RecipeCompilerUnitTest { ingredientNames = listOf("paymentId") ) - val arcs = RecipeCompiler.buildEventLimiterArcs(listOf(event1, event2)) + val arcs = RecipeCompilerKotlin.buildEventLimiterArcs(listOf(event1, event2)) // Sensory events with firing limits should have limiter arcs assertTrue(arcs.isNotEmpty(), "Should create event limiter arcs for sensory events with firing limits") @@ -333,7 +332,7 @@ class RecipeCompilerUnitTest { requiredEvents = setOf("ItemsReserved", "PaymentMade") ) - val arcs = RecipeCompiler.buildEventPreconditionArcs( + val arcs = RecipeCompilerKotlin.buildEventPreconditionArcs( listOf(itemsReservedEvent, paymentMadeEvent), listOf(shipOrderInteraction), listOf(shipOrderDescriptor) @@ -366,7 +365,7 @@ class RecipeCompilerUnitTest { requiredOneOfEvents = setOf(setOf("ItemsReserved", "PaymentReceived")) ) - val arcs = RecipeCompiler.buildEventPreconditionArcs( + val arcs = RecipeCompilerKotlin.buildEventPreconditionArcs( listOf(itemsReservedEvent, paymentReceivedEvent), listOf(shipOrderInteraction), listOf(shipOrderDescriptor) @@ -396,7 +395,7 @@ class RecipeCompilerUnitTest { val reserveDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ReserveItems") val processDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ProcessPayment") - val arcs = RecipeCompiler.buildEventPreconditionArcs( + val arcs = RecipeCompilerKotlin.buildEventPreconditionArcs( emptyList(), listOf(reserveItemsInteraction, processPaymentInteraction), listOf(reserveDescriptor, processDescriptor) @@ -424,7 +423,7 @@ class RecipeCompilerUnitTest { requiredEvents = setOf("ItemsReserved", "PaymentReceived") ) - val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) + val errors = RecipeCompilerKotlin.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) // ShipOrder requires PaymentReceived which doesn't exist assertTrue(errors.isNotEmpty(), "Should report missing required event") @@ -449,7 +448,7 @@ class RecipeCompilerUnitTest { requiredOneOfEvents = setOf(setOf("ItemsReserved", "PaymentReceived")) ) - val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) + val errors = RecipeCompilerKotlin.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) // ShipOrder requires ItemsReserved OR PaymentReceived, neither exists assertTrue(errors.size >= 2, "Should report all missing OR events") @@ -476,7 +475,7 @@ class RecipeCompilerUnitTest { requiredEvents = setOf("ItemsReserved", "PaymentReceived") ) - val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) + val errors = RecipeCompilerKotlin.buildPreconditionErrors(transitions, listOf(shipOrderDescriptor)) // All required events exist assertEquals(0, errors.size, "Should not report errors when all required events exist") @@ -498,7 +497,7 @@ class RecipeCompilerUnitTest { val reserveDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ReserveItems") val processDescriptor = TestDataBuilders.interactionDescriptorWithPreconditions("ProcessPayment") - val errors = RecipeCompiler.buildPreconditionErrors(transitions, listOf(reserveDescriptor, processDescriptor)) + val errors = RecipeCompilerKotlin.buildPreconditionErrors(transitions, listOf(reserveDescriptor, processDescriptor)) // No interactions have required events assertEquals(0, errors.size, "Should not report errors when no preconditions are defined") @@ -513,7 +512,7 @@ class RecipeCompilerUnitTest { ingredientNames = listOf("orderId", "items") ) - val arcs = RecipeCompiler.buildSensoryEventArcs(listOf(orderPlacedEvent), emptyList()) + val arcs = RecipeCompilerKotlin.buildSensoryEventArcs(listOf(orderPlacedEvent), emptyList()) // OrderPlaced has orderId and items ingredients assertTrue(arcs.size >= 2, "Should create arcs for each ingredient") @@ -532,11 +531,11 @@ class RecipeCompilerUnitTest { ) // Build facilitator transitions for shared ingredients - val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + val facilitatorTransitions = RecipeCompilerKotlin.buildMultipleOutputFacilitatorTransitions( listOf(reserveItemsTransition, processPaymentTransition) ) - val arcs = RecipeCompiler.buildMultipleOutputFacilitatorArcs(facilitatorTransitions) + val arcs = RecipeCompilerKotlin.buildMultipleOutputFacilitatorArcs(facilitatorTransitions) // Should have facilitator arcs for shared ingredients (orderId) assertTrue(arcs.isNotEmpty(), "Should create facilitator arcs") @@ -563,11 +562,11 @@ class RecipeCompilerUnitTest { val paymentReceivedEvent = TestDataBuilders.eventTransition("PaymentReceived", isSensory = false) // Build facilitator transitions - val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + val facilitatorTransitions = RecipeCompilerKotlin.buildMultipleOutputFacilitatorTransitions( listOf(reserveItemsTransition, processPaymentTransition) ) - val arcs = RecipeCompiler.buildInteractionArcs( + val arcs = RecipeCompilerKotlin.buildInteractionArcs( listOf(reserveItemsTransition, processPaymentTransition), facilitatorTransitions, listOf(itemsReservedEvent, paymentReceivedEvent) @@ -591,7 +590,7 @@ class RecipeCompilerUnitTest { inputIngredientNames = listOf("orderId", "paymentId") ) - val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + val facilitatorTransitions = RecipeCompilerKotlin.buildMultipleOutputFacilitatorTransitions( listOf(reserveItemsTransition, processPaymentTransition) ) @@ -608,7 +607,7 @@ class RecipeCompilerUnitTest { inputIngredientNames = listOf("orderId", "items") ) - val facilitatorTransitions = RecipeCompiler.buildMultipleOutputFacilitatorTransitions( + val facilitatorTransitions = RecipeCompilerKotlin.buildMultipleOutputFacilitatorTransitions( listOf(reserveItemsTransition) ) diff --git a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt similarity index 99% rename from core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt rename to core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt index 5f19fcca0..6af566a35 100644 --- a/core/baker-compiler/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt +++ b/core/baker-compiler-kotlin/src/test/kotlin/com/ing/baker/compiler/TestDataBuilders.kt @@ -112,7 +112,7 @@ object TestDataBuilders { ) // Reuse RecipeCompiler.interactionTransitionOf - return RecipeCompiler.interactionTransitionOf( + return RecipeCompilerKotlin.interactionTransitionOf( descriptor, InteractionFailureStrategy.BlockInteraction(), inputIngredientNames.toSet() diff --git a/core/baker-compiler/pom.xml b/core/baker-compiler/pom.xml index c395fe315..073c920c0 100644 --- a/core/baker-compiler/pom.xml +++ b/core/baker-compiler/pom.xml @@ -16,6 +16,11 @@ Compiler for Baker recipes + + com.ing.baker + baker-compiler-kotlin + ${project.version} + com.ing.baker baker-recipe-dsl @@ -32,6 +37,11 @@ ${project.version} + + org.scala-lang + scala-library + + com.ing.baker @@ -41,6 +51,16 @@ test + + org.scalatest + scalatest_${scala.compat.version} + test + + + org.scalacheck + scalacheck_${scala.compat.version} + test + org.junit.jupiter junit-jupiter-engine @@ -55,25 +75,15 @@ + src/main/kotlin + src/test/kotlin - + org.codehaus.mojo build-helper-maven-plugin ${build.helper.maven.plugin.version} - - add-source - generate-sources - - add-source - - - - src/main/kotlin - - - add-test-source generate-test-sources @@ -106,6 +116,7 @@ test-compile + process-test-sources test-compile @@ -113,50 +124,9 @@ - - org.apache.maven.plugins - maven-jar-plugin - ${maven.jar.plugin.version} - - - create-test-jar - package - - test-jar - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven.surefire.plugin.version} - - false - false - false - plain - - false - - - **/*Test.class - **/*Tests.class - - - **/*Spec.class - - - - - test - - test - - - + org.scalatest + scalatest-maven-plugin diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt index be553bfca..041686e05 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt @@ -1,119 +1,11 @@ package com.ing.baker.compiler -import com.ing.baker.compiler.PreCompileValidations.preCompileAssertions -import com.ing.baker.compiler.ScalaConversions.asJava -import com.ing.baker.compiler.ScalaConversions.asScala import com.ing.baker.il.CompiledRecipe -import com.ing.baker.il.EventDescriptor -import com.ing.baker.il.IngredientDescriptor -import com.ing.baker.il.RecipeValidations import com.ing.baker.il.ValidationSettings -import com.ing.baker.il.failurestrategy.`BlockInteraction$` -import com.ing.baker.il.failurestrategy.FireEventAfterFailure -import com.ing.baker.il.failurestrategy.FireFunctionalEventAfterFailure -import com.ing.baker.il.failurestrategy.RetryWithIncrementalBackoff -import com.ing.baker.il.petrinet.Edge -import com.ing.baker.il.petrinet.EventTransition -import com.ing.baker.il.petrinet.InteractionTransition -import com.ing.baker.il.petrinet.IntermediateTransition -import com.ing.baker.il.petrinet.MissingEventTransition -import com.ing.baker.il.petrinet.MultiFacilitatorTransition -import com.ing.baker.il.petrinet.Place -import com.ing.baker.il.petrinet.Place.FiringLimiterPlace -import com.ing.baker.il.petrinet.Transition -import com.ing.baker.petrinet.api.PetriNet -import com.ing.baker.recipe.CheckPointEvent -import com.ing.baker.recipe.Recipe -import com.ing.baker.recipe.Sieve -import com.ing.baker.recipe.Event -import com.ing.baker.recipe.EventOutputTransformer -import com.ing.baker.recipe.Ingredient -import com.ing.baker.recipe.Interaction -import com.ing.baker.recipe.InteractionDescriptor -import com.ing.baker.recipe.common.InteractionFailureStrategy -import com.ing.baker.recipe.common.Recipe as ScalaRecipe -import com.ing.baker.recipe.toKotlin -import com.ing.baker.types.`NullValue$` -import com.ing.baker.types.OptionType -import com.ing.baker.types.Type -import com.ing.baker.types.Value -import scala.Option -import scala.Some -import scala.collection.immutable.Seq -import scala.reflect.ClassTag -import scala.util.Either -import scala.util.Left -import scala.util.Right -import scalax.collection.Graph -import scalax.collection.`Graph$` -import scalax.collection.GraphEdge -import scalax.collection.config.CoreConfig -import scalax.collection.config.GraphConfig -import scalax.collection.edge.WLDiEdge -import scalax.collection.edge.`WLDiEdge$` -import scalax.collection.mutable.ArraySet -import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` as Scala212CompatibleScala -import com.ing.baker.il.failurestrategy.InteractionFailureStrategy as ILInteractionFailureStrategy -import com.ing.baker.il.`package$`.`MODULE$` as ILPackage -import com.ing.baker.il.petrinet.Place.`EmptyEventIngredientPlace$`.`MODULE$` as EmptyEventIngredientPlace -import com.ing.baker.il.petrinet.Place.`EventPreconditionPlace$`.`MODULE$` as EventPreconditionPlace -import com.ing.baker.il.petrinet.Place.`IngredientPlace$`.`MODULE$` as IngredientPlace -import com.ing.baker.il.petrinet.Place.`InteractionEventOutputPlace$`.`MODULE$` as InteractionEventOutputPlace -import com.ing.baker.il.petrinet.Place.`IntermediatePlace$`.`MODULE$` as IntermediatePlace -import com.ing.baker.il.petrinet.Place.`MultiTransitionPlace$`.`MODULE$` as MultiTransitionPlace - -/** - * Type alias for the node type of the scalax.collection.Graph backing the petri net. - */ -typealias Node = Either - -/** - * Type alias for the edge type of the scalax.collection.Graph backing the petri net. - */ -typealias Arc = WLDiEdge - -/** - * Type alias for a multi set. - */ -typealias MultiSet = Map - -/** - * Type alias for a marking. - */ -typealias Marking = Map> - -/** - * Holds all components prepared from the recipe during the preparation phase. - */ -internal data class RecipeComponents( - val actionDescriptors: List, - val sensoryEvents: Set, - val allIngredientNames: Set -) - -/** - * Holds all transition types built during the transition building phase. - */ -internal data class TransitionCollections( - val allInteractionTransitions: List, - val sensoryEventTransitions: List, - val interactionEventTransitions: List, - val allEventTransitions: List, - val multipleOutputFacilitatorTransitions: List -) +import com.ing.baker.recipe.common.Recipe object RecipeCompiler { - /** - * Compile the given recipe to a technical recipe that is useful for Baker. - * - * @param recipe ; The Recipe to compile and execute - * @return - */ - @JvmStatic - fun compileRecipe(recipe: ScalaRecipe): CompiledRecipe = - compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) - /** * Compile the given recipe to a technical recipe that is useful for Baker. * @@ -123,846 +15,10 @@ object RecipeCompiler { */ @JvmStatic fun compileRecipe( - recipe: ScalaRecipe, - validationSettings: ValidationSettings - ): CompiledRecipe = - compileRecipe(recipe.toKotlin(), validationSettings) - - /** - * Compile the given recipe to a technical recipe that is useful for Baker. - * - * @param recipe ; The Recipe to compile and execute - * @param validationSettings The validation settings to follow for the validation - * @return - */ - @JvmStatic - fun compileRecipe( - recipe: Recipe, - validationSettings: ValidationSettings - ): CompiledRecipe { - - val precompileErrors: List = preCompileAssertions(recipe) - - val (actionDescriptors, sensoryEvents, allIngredientNames) = prepareRecipeComponents(recipe) - - val transitions = buildAllTransitions( - actionDescriptors, - allIngredientNames, - sensoryEvents, - recipe.defaultFailureStrategy - ) - - val arcs = buildAllPetriNetArcs(actionDescriptors, transitions) - - val preconditionErrors = buildPreconditionErrors(transitions, actionDescriptors) - - return assemblePetriNetAndValidate(recipe, arcs, precompileErrors, preconditionErrors, validationSettings) - } - /** - * Gathers all components from the recipe including action descriptors, sensory events, and ingredient names. - */ - internal fun prepareRecipeComponents(recipe: Recipe): RecipeComponents { - // Extend the interactions with the checkpoint event interactions and sub-recipes - val actionDescriptors: List = recipe.interactions + - recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + - recipe.sieves.map(::convertSieveToInteraction) + - recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) - - val sensoryEvents = flattenSensoryEvents(recipe) - - //All ingredient names provided by sensory events or by interactions - val allIngredientNames: Set = - sensoryEvents - .flatMap { e -> e.providedIngredients.map(Ingredient::name) } - .toSet() + - actionDescriptors.flatMap { i -> - i.output.flatMap { e -> - // check if the event was renamed (check if there is a transformer for this event) - when (val transformer = i.eventOutputTransformers[e]) { - null -> e.providedIngredients.map(Ingredient::name) - else -> e.providedIngredients.map { ingredient -> - transformer.ingredientRenames[ingredient.name] ?: ingredient.name - } - } - } - } - - return RecipeComponents(actionDescriptors, sensoryEvents, allIngredientNames) - } - - /** - * Creates interaction transitions, event transitions, and facilitator transitions. - */ - internal fun buildAllTransitions( - actionDescriptors: List, - allIngredientNames: Set, - sensoryEvents: Set, - failureStrategy: InteractionFailureStrategy, - ): TransitionCollections { - // For inputs for which no matching output cannot be found, we do not want to generate a place. - // It should be provided at runtime from outside the active petri net (marking) - val allInteractionTransitions: List = - actionDescriptors.map { interactionTransitionOf(it, failureStrategy, allIngredientNames) } - - // events provided from outside - val sensoryEventTransitions: List = sensoryEvents.map { event -> - EventTransition( - EventDescriptor( - event.name, - event.providedIngredients.map { IngredientDescriptor(it.name, it.type) }.asScala - ), true, event.maxFiringLimit.asScala - ) - } - - // events provided by other transitions / actions - val interactionEventTransitions: List = allInteractionTransitions.flatMap { t -> - t.eventsToFire().asJava.map { event -> EventTransition(event, false, Option.empty()) } - } - - val allEventTransitions: List = sensoryEventTransitions + interactionEventTransitions - - val multipleOutputFacilitatorTransitions: List = - buildMultipleOutputFacilitatorTransitions(allInteractionTransitions) - - return TransitionCollections( - allInteractionTransitions, - sensoryEventTransitions, - interactionEventTransitions, - allEventTransitions, - multipleOutputFacilitatorTransitions - ) - } - - /** - * Builds internal event arcs that connect interaction transitions to ingredient places. - */ - internal fun buildInternalEventArcs( - allInteractionTransitions: List, - interactionEventTransitions: List - ): List { - // Given the event classes, it is creating the ingredient places and - // connecting a transition to a ingredient place. - val internalEventArcs: List = allInteractionTransitions.flatMap { t -> - t.eventsToFire.flatMap { event -> - event.ingredients.map { ingredient -> - val from = - interactionEventTransitions.find { it.label() == event.name() } - ?: throw RecipeValidationException() - arc(from, createPlace(ingredient.name(), IngredientPlace)) - } - } - } - return internalEventArcs - } - - /** - * Builds event limiter arcs for sensory events with firing limits. - */ - internal fun buildEventLimiterArcs(sensoryEventTransitions: List): List { - val eventLimiterArcs = sensoryEventTransitions.flatMap { t -> - when (val n = t.maxFiringLimit().getOrElse { null as Int? }) { - null -> emptyList() - else -> listOf(arc(createPlace("limit:${t.label()}", FiringLimiterPlace(n)), t)) - } - } - - return eventLimiterArcs - } - - /** - * Builds event precondition arcs (AND and OR) for all interactions. - */ - internal fun buildEventPreconditionArcs( - allEventTransitions: List, - allInteractionTransitions: List, - actionDescriptors: List - ): List { - fun findEventTransitionByEventName(eventName: String) = - allEventTransitions.find { it.event().name() == eventName } - - fun findInteractionByLabel(label: String) = - allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() - - // This generates precondition arcs for Required Events (AND). - val eventPreconditionArcs = actionDescriptors.flatMap { t -> - buildEventAndPreconditionArcs( - t, - ::findEventTransitionByEventName, - ::findInteractionByLabel - ) - } - - // This generates precondition arcs for Required Events (OR). - val eventOrPreconditionArcs = actionDescriptors.flatMap { t -> - buildEventORPreconditionArcs(t, ::findEventTransitionByEventName, ::findInteractionByLabel) - } - return eventPreconditionArcs + eventOrPreconditionArcs - } - - /** - * Builds sensory event arcs connecting sensory events to ingredient places or empty places. - */ - internal fun buildSensoryEventArcs( - sensoryEventTransitions: List, - actionDescriptors: List - ): List { - val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition { it.event().ingredients.isEmpty() } - - // It connects a sensory event to an ingredient places - val sensoryEventArcs: List = sensoryEventWithIngredients.flatMap { et -> - et.event().ingredients.map { ingredient -> - arc(et, createPlace(ingredient.name(), IngredientPlace)) - } - } - - val eventThatArePreconditions: List = actionDescriptors.flatMap { actionDescriptor -> - actionDescriptor.requiredEvents + actionDescriptor.requiredOneOfEvents.flatten() - } - - // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net - val sensoryEventArcsNoIngredientsArcs: List = sensoryEventWithoutIngredients - //Filter out events that are preconditions to interactions - .filterNot { sensoryEvent -> eventThatArePreconditions.contains(sensoryEvent.label()) } - .map { sensoryEvent -> - arc(sensoryEvent, createPlace(sensoryEvent.label(), EmptyEventIngredientPlace)) - } - return sensoryEventArcs + sensoryEventArcsNoIngredientsArcs - } - - - /** - * Builds facilitator transitions for ingredients with multiple consumers. - */ - internal fun buildMultipleOutputFacilitatorTransitions(allInteractionTransitions: List): List { - // First find the cases where multiple transitions depend on the same ingredient place - val ingredientsWithMultipleConsumers: Map> = - getIngredientsWithMultipleConsumers(allInteractionTransitions) - - // Add one new transition for each duplicate input (the newly added one in the image above) - val multipleOutputFacilitatorTransitions: List = - ingredientsWithMultipleConsumers.keys.map(::MultiFacilitatorTransition) - return multipleOutputFacilitatorTransitions - } - - /** - * Builds facilitator arcs for ingredients with multiple consumers. - */ - internal fun buildMultipleOutputFacilitatorArcs(multipleOutputFacilitatorTransitions: List): List = - multipleOutputFacilitatorTransitions.map { t -> - arc(createPlace(t.label(), IngredientPlace), t) - } - - /** - * Builds interaction arcs for all interactions in the recipe. - */ - internal fun buildInteractionArcs( - allInteractionTransitions: List, - multipleOutputFacilitatorTransitions: List, - interactionEventTransitions: List - ): List { - val ingredientsWithMultipleConsumers = getIngredientsWithMultipleConsumers(allInteractionTransitions) - - return allInteractionTransitions.flatMap { interactionTransition -> - buildInteractionArcs( - multipleOutputFacilitatorTransitions, - ingredientsWithMultipleConsumers, - interactionEventTransitions, - interactionTransition - ) - } - } - - /** - * Consolidates all arc building logic. Arc order is preserved for backward compatibility. - */ - internal fun buildAllPetriNetArcs( - actionDescriptors: List, - transitions: TransitionCollections, - ): List { - val (allInteractionTransitions, sensoryEventTransitions, interactionEventTransitions, allEventTransitions, multipleOutputFacilitatorTransitions) = transitions - - val interactionArcs = buildInteractionArcs(allInteractionTransitions, multipleOutputFacilitatorTransitions, interactionEventTransitions) - - val eventPreconditionArcs = buildEventPreconditionArcs(allEventTransitions, allInteractionTransitions, actionDescriptors) - - val eventLimiterArcs: List = buildEventLimiterArcs(sensoryEventTransitions) - - val sensoryEventArcs = buildSensoryEventArcs(sensoryEventTransitions, actionDescriptors) - - val internalEventArcs: List = buildInternalEventArcs(allInteractionTransitions, interactionEventTransitions) - - val multipleOutputFacilitatorArcs = buildMultipleOutputFacilitatorArcs(multipleOutputFacilitatorTransitions) - - val arcs = (interactionArcs + - eventPreconditionArcs + - eventLimiterArcs + - sensoryEventArcs + - internalEventArcs + - multipleOutputFacilitatorArcs) - return arcs - } - - /** - * Constructs the final Petri net from transitions, arcs, places, and initial marking. - */ - internal fun assemblePetriNetAndValidate( recipe: Recipe, - arcs: List, - precompileErrors: List, - preconditionErrors: List, validationSettings: ValidationSettings - ): CompiledRecipe { - val petriNet = PetriNet(graph(arcs)) + ): CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe, validationSettings) - val initialMarking: Marking = petriNet.places().asJava.mapNotNull { p -> - when (val placeType = p.placeType()) { - is FiringLimiterPlace -> p to mapOf(null to placeType.maxLimit()) - else -> null - } - }.toMap() - - val errors = preconditionErrors + precompileErrors - - val compiledRecipe = CompiledRecipe.build( - recipe.name, - petriNet, - initialMarking.mapValues { it.value.mapValues { it.value as Any }.asScala }.asScala, - errors.asScala, - recipe.eventReceivePeriod.asScala, - recipe.retentionPeriod.asScala, - Scala212CompatibleScala, - ) - - return RecipeValidations.postCompileValidations(compiledRecipe, validationSettings) - } - - private fun transition(transition: Transition) = Right(transition) - private fun place(place: Place) = Left(place) - - private fun arc(t: Transition, p: Place): Arc = - wlDiEdge(transition(t), place(p), Edge(Option.empty())) - - private fun arc(p: Place, t: Transition, eventFilter: String? = null): Arc = - wlDiEdge(place(p), transition(t), Edge(Option.apply(eventFilter))) - - /** - * Creates a transition for a missing event in the recipe. - * Creates a missing event transition placeholder. - * Used when an interaction requires an event that doesn't exist in the recipe. - */ - internal fun missingEventTransition(eventName: String) = MissingEventTransition(eventName) - - private fun buildEventAndPreconditionErrors( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition - ) = - // Find the event in available events - interaction.requiredEvents.flatMap { eventName -> - buildEventPreconditionErrors( - eventName, - preconditionTransition, - interactionTransition(interaction.name) - ) - } - - private fun buildEventORPreconditionErrors( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition - ) = interaction.requiredOneOfEvents.flatMapIndexed { index: Int, orGroup: Set -> - orGroup.flatMap { eventName -> - buildEventPreconditionErrors( - eventName, - preconditionTransition, - interactionTransition(interaction.name) - ) - } - } - - private fun buildEventPreconditionErrors( - eventName: String, - preconditionTransition: (String) -> Transition?, - interactionTransition: Transition - ): List { - - val eventTransition = preconditionTransition(eventName) - - val notProvidedError = when (eventTransition) { - null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") - else -> emptyList() - } - - return notProvidedError - } - - /** - * Builds a list of error messages for missing required events. - * Validates that all event preconditions (AND and OR) are satisfied. - * - * @param transitions All transitions in the recipe - * @param actionDescriptors All interaction descriptors in the recipe - * @return List of error messages for missing required events - */ - internal fun buildPreconditionErrors( - transitions: TransitionCollections, - actionDescriptors: List - ): List { - fun findEventTransitionByEventName(eventName: String) = - transitions.allEventTransitions.find { it.event().name() == eventName } - - fun findInteractionByLabel(label: String) = - transitions.allInteractionTransitions.find { it.label() == label } ?: throw RecipeValidationException() - - // This generates precondition errors for Required Events (AND). - val preconditionANDErrors = actionDescriptors.flatMap { t -> - buildEventAndPreconditionErrors(t, ::findEventTransitionByEventName, ::findInteractionByLabel) - } - - // This generates precondition errors for Required Events (OR). - val preconditionORErrors = actionDescriptors.flatMap { t -> - buildEventORPreconditionErrors(t, ::findEventTransitionByEventName, ::findInteractionByLabel) - } - return preconditionORErrors + preconditionANDErrors - } - - private fun buildEventAndPreconditionArcs( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition - ) = - // Find the event in available events - interaction.requiredEvents.flatMap { eventName -> - // a new `Place` generated for each AND events - val eventPreconditionPlace = - createPlace( - label = "$eventName-${interaction.name}", - placeType = EventPreconditionPlace - ) - buildEventPreconditionArcs( - eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name) - ) - } - - private fun buildEventORPreconditionArcs( - interaction: InteractionDescriptor, - preconditionTransition: (String) -> Transition?, - interactionTransition: (String) -> Transition - ) = interaction.requiredOneOfEvents.flatMapIndexed { index: Int, orGroup: Set -> - // only one `Place` for all the OR events - val eventPreconditionPlace = createPlace( - label = "${interaction.name}-or-$index", - placeType = EventPreconditionPlace - ) - orGroup.flatMap { eventName -> - buildEventPreconditionArcs( - eventName, - eventPreconditionPlace, - preconditionTransition, - interactionTransition(interaction.name) - ) - } - } - - private fun buildEventPreconditionArcs( - eventName: String, - preconditionPlace: Place, - preconditionTransition: (String) -> Transition?, - interactionTransition: Transition - ): List { - - val eventTransition = preconditionTransition(eventName) - - val arcs = listOf( - arc(eventTransition ?: missingEventTransition(eventName), preconditionPlace), - arc(preconditionPlace, interactionTransition) - ) - - return arcs - } - - // the (possible) event output arcs / places - private fun buildInteractionOutputArcs( - interaction: InteractionTransition, - eventTransitions: List - ) = - if (interaction.eventsToFire.isNotEmpty()) { - val resultPlace = - createPlace(label = interaction.label(), placeType = InteractionEventOutputPlace) - val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor -> - //Get the correct event transition - val eventTransition = eventTransitions.find { it.event().name() == event.name() } - ?: throw RecipeValidationException("eventTransition should be found") - //Decide if there are multiple interactions that fire this transition, - // if so create a event combiner place - // else link the transition to the event. - val eventTransitionCount = eventTransitions.count { e -> e.event().name() == event.name() } - if (eventTransitionCount > 1) { - //Create a new intermediate event place - val eventCombinerPlace: Place = - createPlace(label = event.name(), placeType = IntermediatePlace) - //Create a new intermediate event transition - val interactionToEventTransition: IntermediateTransition = - IntermediateTransition("${interaction.interactionName()}:${event.name()}") - //link the interaction output place to the intermediate transition - val interactionOutputPlaceToIntermediateTransition: Arc = - arc(resultPlace, interactionToEventTransition, event.name()) - //link the intermediate transition to the intermediate input place - val intermediateTransitionToEventCombinerPlace: Arc = - arc(interactionToEventTransition, eventCombinerPlace) - //Link the intermediate place to the event place - val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition) - listOf( - intermediateTransitionToEventCombinerPlace, - interactionOutputPlaceToIntermediateTransition, - eventCombinerPlaceToEventTransition - ) - } else { - listOf(arc(resultPlace, eventTransition, event.name())) - } - } - eventArcs + arc(interaction, resultPlace) - } else emptyList() - - /** - * Draws an arc from all required ingredients for an interaction - * If the ingredient has multiple consumers create a multi transition place and create both arcs for it - */ - private fun buildInteractionInputArcs( - t: InteractionTransition, - multipleOutputFacilitatorTransitions: List, - ingredientsWithMultipleConsumers: Map> - ): List { - - val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = - t.nonProvidedIngredients.map(IngredientDescriptor::name) - .partition(ingredientsWithMultipleConsumers::contains) - - // the extra arcs to model multiple output transitions from one place - val internalDataInputArcs = fieldNamesWithPrefixMulti.flatMap { fieldName -> - val multiTransitionPlace = - createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace) - listOf( - // one arc from multiplier place to the transition - arc(getMultiTransition(fieldName, multipleOutputFacilitatorTransitions), multiTransitionPlace), - // one arc from extra added place to transition - arc(multiTransitionPlace, t) - ) - } - - // the data input arcs / places - val dataInputArcs: List = fieldNamesWithoutPrefix.map { fieldName -> - arc(createPlace(fieldName, IngredientPlace), t) - } - - val dataOutputArcs: List = - if (t.isReprovider) - fieldNamesWithoutPrefix.map { fieldName -> - arc(t, createPlace(fieldName, IngredientPlace)) - } + fieldNamesWithPrefixMulti.map { fieldName -> - arc(t, createPlace("${t.label()}-$fieldName", placeType = MultiTransitionPlace)) - } - else - emptyList() - - val limitInteractionCountArc: List = when (val maximumInteractionCount = t.maximumInteractionCount) { - null -> emptyList() - else -> listOf( - arc(createPlace("limit:${t.label()}", FiringLimiterPlace(maximumInteractionCount)), t) - ) - } - - return dataInputArcs + dataOutputArcs + internalDataInputArcs + limitInteractionCountArc - } - - private fun buildInteractionArcs( - multipleOutputFacilitatorTransitions: List, - placeNameWithDuplicateTransitions: Map>, - eventTransitions: List, interactionTransition: InteractionTransition - ) = - buildInteractionInputArcs( - interactionTransition, - multipleOutputFacilitatorTransitions, - placeNameWithDuplicateTransitions - ) + buildInteractionOutputArcs( - interactionTransition, - eventTransitions - ) - - /** - * Finds a multi-transition (facilitator transition) by its internal representation name. - */ - private fun getMultiTransition(internalRepresentationName: String, transitions: List) = - transitions.find { it.label().equals(internalRepresentationName) } - ?: throw NoSuchElementException("No multi transition found with name $internalRepresentationName") - - /** - * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. - * - * @param actionTransitions Seq of reflected transition. - * @return A map from input place name to reflected transitions (where the transitions have as input the place). - */ - private fun getIngredientsWithMultipleConsumers(actionTransitions: List): Map> = - // Obtain a list of field name with their transition - actionTransitions - .flatMap { transition -> - transition.nonProvidedIngredients.map { ingredient -> - ingredient.name() to transition - } - } - .groupBy({ it.first }, { it.second }) - // Only keep those place names which have more than one out-adjacent transition - .filter { (_, interactions) -> interactions.size >= 2 } - - /** - * Creates a Place with the given label and type. - * The label is prefixed with the place type's prefix. - * Creates a Place in the Petri net with the appropriate label prefix. - */ - internal fun createPlace(label: String, placeType: Place.PlaceType): Place = - Place("${placeType.labelPrepend()}$label", placeType) - - /** - * Converts a CheckPointEvent into an InteractionDescriptor. - * Checkpoint events are special events that can be used to track progress in a recipe. - * They are converted to interactions with no input ingredients. - */ - private fun convertCheckpointEventToInteraction(e: CheckPointEvent) = - Interaction( - name = "${ILPackage.checkpointEventInteractionPrefix()}${e.name}", - inputIngredients = emptyList(), - output = listOf(Event(e.name, emptyList())), - requiredEvents = e.requiredEvents, - requiredOneOfEvents = e.requiredOneOfEvents - ) - - /** - * Converts a Sieve into an InteractionDescriptor. - * Sieves are interactions that filter/transform ingredients without requiring specific events. - */ - private fun convertSieveToInteraction(s: Sieve) = - Interaction( - name = "${ILPackage.sieveInteractionPrefix()}${s.name}", - inputIngredients = s.inputIngredients, - output = s.output - ) - - /** - * Flattens sub-recipes into InteractionDescriptors. - * Each interaction in a sub-recipe is copied and prefixed to avoid name conflicts. - */ - private fun flattenSubRecipesToInteraction(recipe: Recipe): Set { - fun copyInteraction(i: InteractionDescriptor) = com.ing.baker.recipe.Interaction( - $$"$${ILPackage.subRecipePrefix()}$${recipe.name}$$${i.name}", - i.originalName, - i.inputIngredients, - i.output, - i.requiredEvents, - i.requiredOneOfEvents, - i.predefinedIngredients, - i.overriddenIngredientNames, - i.overriddenOutputIngredientName, - i.eventOutputTransformers, - i.maximumInteractionCount, - i.failureStrategy, - i.isReprovider, - ) - return recipe.interactions.map(::copyInteraction).toSet() + - recipe.checkpointEvents.map(::convertCheckpointEventToInteraction) + - recipe.subRecipes.flatMap(::flattenSubRecipesToInteraction) + - recipe.sieves.map(::convertSieveToInteraction) - } - - /** - * Flattens sensory events from a recipe including events from sub-recipes recursively. - */ - private fun flattenSensoryEvents(recipe: Recipe): Set = - recipe.sensoryEvents + recipe.subRecipes.flatMap(::flattenSensoryEvents) - - /** - * Creates a Petri net graph from a list of arcs. - * Uses the Scala graph library to construct the graph structure. - */ - private fun graph(arcs: List): Graph> = - `Graph$`.`MODULE$`.from( - arcs.map { it as WLDiEdge }.asScala, - ClassTag.apply(WLDiEdge::class.java), - CoreConfig(GraphConfig.defaultOrder(), ArraySet.`Hints$`.`MODULE$`.apply(16, 32, 48, 80)) - ) as Graph> - - /** - * Creates a weighted labeled directed edge between two nodes. - * Used to create arcs in the Petri net graph. - */ - private fun wlDiEdge(node1: Node, node2: Node, label: L): WLDiEdge = - `WLDiEdge$`.`MODULE$`.newEdge(GraphEdge.`NodeProduct$`.`MODULE$`.apply(node1, node2), 1.0, label) - - /** - * Converts an InteractionDescriptor to an InteractionTransition for use in the Petri net. - * - * This function transforms recipe-level interaction descriptors into IL-level transitions, - * handling event transformations, ingredient mapping, optional ingredients, and failure strategies. - */ - internal fun interactionTransitionOf( - interactionDescriptor: InteractionDescriptor, - defaultFailureStrategy: InteractionFailureStrategy, - allIngredientNames: Set - ): InteractionTransition { - //This transforms the event using the eventOutputTransformer to the new event - //If there is no eventOutputTransformer for the event the original event is returned - fun transformEventType(event: Event): Event = - when (val eventOutputTransformer = interactionDescriptor.eventOutputTransformers[event]) { - null -> event - else -> Event( - eventOutputTransformer.newEventName, - event.providedIngredients.map { i -> - Ingredient( - eventOutputTransformer.ingredientRenames.getOrElse(i.name, { i.name }), - i.type - ) - }, - null - ) - } - - fun transformEventOutputTransformer(recipeEventOutputTransformer: EventOutputTransformer): com.ing.baker.il.EventOutputTransformer = - com.ing.baker.il.EventOutputTransformer( - recipeEventOutputTransformer.newEventName, - recipeEventOutputTransformer.ingredientRenames.asScala - ) - - fun transformEventToCompiledEvent(event: Event): EventDescriptor = - EventDescriptor( - event.name, - event.providedIngredients.map { IngredientDescriptor(it.name, it.type) }.asScala - ) - - // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe - // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe - // Replace BakerEventList to BakerEventList tag as know in compiledRecipe - // Replace ingredient tags with overridden tags - val inputFields: Seq> = interactionDescriptor.inputIngredients - .map { ingredient -> - when (ingredient.name) { - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceIdName() -> ILPackage.recipeInstanceIdName() - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceMetadataName() -> ILPackage.recipeInstanceMetadataName() - com.ing.baker.recipe.common.`package$`.`MODULE$`.recipeInstanceEventListName() -> ILPackage.recipeInstanceEventListName() - else -> interactionDescriptor.overriddenIngredientNames - .getOrElse(ingredient.name, { ingredient.name }) - } to ingredient.type - }.asScala - - val originalEvents = interactionDescriptor.output.map(::transformEventToCompiledEvent).asScala - val eventsToFire = - interactionDescriptor.output.map(::transformEventType).map(::transformEventToCompiledEvent).asScala - - //For each ingredient that is not provided - //And is of the type Optional or Option - //Add it to the predefinedIngredients List as empty - //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. - val predefinedIngredientsWithOptionalsEmpty: Map = - inputFields.asJava - .filter { (name, type) -> type is OptionType && name !in allIngredientNames } - .associate { Pair(it.first, `NullValue$`.`MODULE$`) } + - interactionDescriptor.predefinedIngredients - - val p: Triple, Option> = - when (val strategy = interactionDescriptor.failureStrategy ?: defaultFailureStrategy) { - is InteractionFailureStrategy.RetryWithIncrementalBackoff -> { - val exhaustedRetryEvent = when (val e = strategy.fireRetryExhaustedEvent()) { - is Some -> Some( - EventDescriptor( - e.value().getOrElse { null as String? } - ?: (interactionDescriptor.name + ILPackage.exhaustedEventAppend()), - emptyList().asScala) - ) - - else -> Option.empty() - } - val functionalFailedEvent = when (val e = strategy.fireFunctionalEvent()) { - is Some -> Some( - EventDescriptor( - e.value().getOrElse { null as String? } - ?: (interactionDescriptor.name + ILPackage.functionalFailedEventAppend()), - emptyList().asScala) - ) - - else -> Option.empty() - } - Triple( - RetryWithIncrementalBackoff( - strategy.initialDelay(), - strategy.backoffFactor(), - strategy.maximumRetries(), - strategy.maxTimeBetweenRetries(), - exhaustedRetryEvent, - functionalFailedEvent, - ), exhaustedRetryEvent, functionalFailedEvent - ) - } - - is InteractionFailureStrategy.BlockInteraction -> Triple( - `BlockInteraction$`.`MODULE$`, - Option.empty(), - Option.empty() - ) - - is InteractionFailureStrategy.FireEventAfterFailure -> { - val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name + ILPackage.exhaustedEventAppend() } - val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) - Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) - } - - is InteractionFailureStrategy.FireEventAndBlock -> { - val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name + ILPackage.exhaustedEventAppend() } - val exhaustedRetryEvent = EventDescriptor(eventName, emptyList().asScala) - Triple(FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), Option.empty()) - } - - is InteractionFailureStrategy.FireEventAndResolve -> { - val eventName = strategy.eventName() - .getOrElse { interactionDescriptor.name + ILPackage.functionalFailedEventAppend() } - val functionalFailed = EventDescriptor(eventName, emptyList().asScala) - Triple(FireFunctionalEventAfterFailure(functionalFailed), Option.empty(), Some(functionalFailed)) - } - - else -> Triple(`BlockInteraction$`.`MODULE$`, Option.empty(), Option.empty()) - } - val failureStrategy = p.first - val exhaustedRetryEvent = p.second.toList().toSeq() - val functionalRetryEvent = p.third.toList().toSeq() - - val eventsToFireAll = eventsToFire.asJava + exhaustedRetryEvent.asJava + functionalRetryEvent.asJava - val originalEventsAll = originalEvents.asJava + exhaustedRetryEvent.asJava + functionalRetryEvent.asJava - - return InteractionTransition( - eventsToFireAll.asScala, - originalEventsAll.asScala, - inputFields.asJava.map { (name, ingredientType) -> IngredientDescriptor(name, ingredientType) }.asScala, - interactionDescriptor.name, - interactionDescriptor.originalName, - predefinedIngredientsWithOptionalsEmpty.asScala, - interactionDescriptor.maximumInteractionCount.asScala, - failureStrategy, - interactionDescriptor.eventOutputTransformers.map { (event, transformer) -> - event.name to transformEventOutputTransformer( - transformer - ) - }.toMap().asScala, - interactionDescriptor.isReprovider - ) - } + @JvmStatic + fun compileRecipe(recipe: Recipe): CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) } - -val InteractionTransition.eventsToFire get() = this.eventsToFire().asJava -val InteractionTransition.nonProvidedIngredients get() = this.nonProvidedIngredients().asJava -val InteractionTransition.maximumInteractionCount - get(): Int? = this.maximumInteractionCount().getOrElse { null as Int? } -val EventDescriptor.ingredients get() = this.ingredients().asJava - - diff --git a/pom.xml b/pom.xml index fcc8ecd2c..b1374856a 100644 --- a/pom.xml +++ b/pom.xml @@ -113,6 +113,7 @@ core/baker-recipe-dsl core/baker-recipe-dsl-kotlin core/baker-compiler + core/baker-compiler-kotlin core/baker-akka-actors core/baker-akka-runtime core/baker-recipe-manager From 622306ca8269ff0026811f6adc06ca925909bc3a Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Tue, 26 May 2026 09:31:25 +0200 Subject: [PATCH 13/16] adding back baker scala compiler --- core/baker-compiler-scala/pom.xml | 147 ++++ .../com/ing/baker/compiler/Assertions.scala | 45 ++ .../baker/compiler/RecipeCompilerScala.scala | 424 ++++++++++++ .../com/ing/baker/compiler/package.scala | 129 ++++ .../src/test/java/RecipeCompilerTests.java | 224 +++++++ .../src/test/kotlin/RecipeCompilerTest.kt | 55 ++ .../baker/compiler/RecipeCompilerSpec.scala | 633 ++++++++++++++++++ core/baker-compiler/pom.xml | 5 + pom.xml | 1 + 9 files changed, 1663 insertions(+) create mode 100644 core/baker-compiler-scala/pom.xml create mode 100644 core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/Assertions.scala create mode 100644 core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala create mode 100644 core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/package.scala create mode 100644 core/baker-compiler-scala/src/test/java/RecipeCompilerTests.java create mode 100644 core/baker-compiler-scala/src/test/kotlin/RecipeCompilerTest.kt create mode 100644 core/baker-compiler-scala/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala diff --git a/core/baker-compiler-scala/pom.xml b/core/baker-compiler-scala/pom.xml new file mode 100644 index 000000000..2f7aaab72 --- /dev/null +++ b/core/baker-compiler-scala/pom.xml @@ -0,0 +1,147 @@ + + + 4.0.0 + + + com.ing.baker + baker + 5.1.0-SNAPSHOT + ../../pom.xml + + + baker-compiler-scala + Baker Recipe Compiler + Compiler for Baker recipes + + + + com.ing.baker + baker-recipe-dsl + ${project.version} + + + com.ing.baker + baker-recipe-dsl-kotlin + ${project.version} + + + com.ing.baker + baker-intermediate-language + ${project.version} + + + + org.scala-lang + scala-library + + + + + com.ing.baker + baker-recipe-dsl + ${project.version} + test-jar + test + + + + org.scalatest + scalatest_${scala.compat.version} + test + + + org.scalacheck + scalacheck_${scala.compat.version} + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin.version} + test + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${build.helper.maven.plugin.version} + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/java + src/test/kotlin + + + + + + + + net.alchim31.maven + scala-maven-plugin + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + ${jvm.target} + + + + compile + process-sources + + compile + + + + test-compile + + test-compile + + + + + + + org.scalatest + scalatest-maven-plugin + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + create-test-jar + package + + test-jar + + + + + + + diff --git a/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/Assertions.scala b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/Assertions.scala new file mode 100644 index 000000000..4ece7fff5 --- /dev/null +++ b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/Assertions.scala @@ -0,0 +1,45 @@ +package com.ing.baker.compiler + +import com.ing.baker.recipe.common.{Event, Ingredient, InteractionDescriptor, Recipe} + +import scala.collection.immutable.Seq + +object Assertions { + private def assertNoDuplicateElementsExist[T](compareIdentifier: T => Any, elements: Set[T]): Unit = elements.foreach { e => + (elements - e).find(c => compareIdentifier(c) == compareIdentifier(e)).foreach { c => throw new IllegalStateException(s"Duplicate identifiers found: ${e.getClass.getSimpleName}:$e and ${c.getClass.getSimpleName}:$c") } + } + + private def assertValidNames[T](nameFunc: T => String, list: Iterable[T], typeName: String): Unit = list.map(nameFunc).filter(name => name == null || name.isEmpty).foreach { _ => + throw new IllegalArgumentException(s"$typeName with a null or empty name found") + } + + private def assertNonEmptyRecipe(recipe: Recipe): Seq[String] = + Seq( + Some("No sensory events found.").filter(_ => recipe.sensoryEvents.isEmpty), + Some("No interactions found.").filter(_ => recipe.interactions.isEmpty) + ).flatten + + private def assertRequiredEventForReprovider(recipe: Recipe): Seq[String] = { + recipe.interactions.filter(interaction => { + interaction.isReprovider && interaction.requiredEvents.isEmpty && interaction.requiredOneOfEvents.isEmpty + }).map(interaction => s"Reprovider interaction ${interaction.name} needs to have a event precondition") + } + + private def assertSensoryEventsNegativeFiringLimits(recipe: Recipe): Seq[String] = + Seq( + Some("MaxFiringLimit should be greater than 0").filter(_ => + recipe.sensoryEvents.flatMap(_.maxFiringLimit).exists(_ <= 0) + ), + ).flatten + + def preCompileAssertions(recipe: Recipe): Seq[String] = { + assertValidNames[Recipe](_.name, Seq(recipe), "Recipe") + assertValidNames[InteractionDescriptor](_.name, recipe.interactions, "Interaction") + assertValidNames[Event](_.name, recipe.sensoryEvents, "Event") + val allIngredients = recipe.sensoryEvents.flatMap(_.providedIngredients) ++ recipe.interactions.flatMap(_.inputIngredients) + assertValidNames[Ingredient](_.name, allIngredients, "Ingredient") + assertNoDuplicateElementsExist[InteractionDescriptor](_.name, recipe.interactions.toSet) + assertNoDuplicateElementsExist[Event](_.name, recipe.sensoryEvents) + assertSensoryEventsNegativeFiringLimits(recipe) ++ assertRequiredEventForReprovider(recipe) + } +} diff --git a/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala new file mode 100644 index 000000000..ae834012f --- /dev/null +++ b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala @@ -0,0 +1,424 @@ +package com.ing.baker +package compiler + +import com.ing.baker.il.CompiledRecipe.{OldRecipeIdVariant, Scala212CompatibleJava, Scala212CompatibleKotlin, Scala212CompatibleScala} +import com.ing.baker.il.RecipeValidations.postCompileValidations +import com.ing.baker.il.petrinet.Place._ +import com.ing.baker.il.petrinet._ +import com.ing.baker.il.{CompiledRecipe, EventDescriptor, ValidationSettings, checkpointEventInteractionPrefix, sieveInteractionPrefix, subRecipePrefix} +import com.ing.baker.petrinet.api._ +import com.ing.baker.recipe.common._ +import com.ing.baker.recipe.{javadsl, kotlindsl} +import com.ing.baker.recipe.scaladsl.{Event, Interaction} +import scalax.collection.edge.WLDiEdge +import scalax.collection.immutable.Graph + +import scala.annotation.nowarn +import scala.language.postfixOps + +object RecipeCompilerScala { + + implicit class TupleSeqOps[A, B](seq: Seq[(Seq[A], Seq[B])]) { + def unzipFlatten: (Seq[A], Seq[B]) = seq.unzip match { + case (a, b) => (a.flatten, b.flatten) + } + } + + @nowarn + def arc(t: Transition, p: Place, weight: Long): Arc = WLDiEdge[Node, Edge](Right(t), Left(p))(weight, Edge(None)) + + @nowarn + def arc(p: Place, t: Transition, weight: Long, eventFilter: Option[String] = None): Arc = { + WLDiEdge[Node, Edge](Left(p), Right(t))(weight, Edge(eventFilter)) + } + + /** + * Creates a transition for a missing event in the recipe. + */ + private def missingEventTransition[E](eventName: String): MissingEventTransition = MissingEventTransition(eventName) + + private def buildEventAndPreconditionArcs(interaction: InteractionDescriptor, + preconditionTransition: String => Option[Transition], + interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { + + //Find the event in available events + + interaction.requiredEvents.toIndexedSeq.map { eventName => + // a new `Place` generated for each AND events + val eventPreconditionPlace = createPlace(label = s"$eventName-${interaction.name}", placeType = EventPreconditionPlace) + + buildEventPreconditionArcs(eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name).get) + }.unzipFlatten + } + + private def buildEventORPreconditionArcs(interaction: InteractionDescriptor, + preconditionTransition: String => Option[Transition], + interactionTransition: String => Option[Transition]): (Seq[Arc], Seq[String]) = { + + interaction.requiredOneOfEvents.toIndexedSeq.zipWithIndex.map { case (orGroup: Set[String], index: Int) => + // only one `Place` for all the OR events + val eventPreconditionPlace = createPlace(label = s"${interaction.name}-or-$index", placeType = EventOrPreconditionPlace) + + orGroup.toIndexedSeq.map { eventName => + buildEventPreconditionArcs(eventName, + eventPreconditionPlace, + preconditionTransition, + interactionTransition(interaction.name).get) + }.unzipFlatten + }.unzipFlatten + } + + private def buildEventPreconditionArcs(eventName: String, + preconditionPlace: Place, + preconditionTransition: String => Option[Transition], + interactionTransition: Transition): (Seq[Arc], Seq[String]) = { + + val eventTransition = preconditionTransition(eventName) + + val notProvidedError = eventTransition match { + case None => Seq(s"Event '$eventName' for '$interactionTransition' is not provided in the recipe") + case Some(_) => Seq.empty + } + + val arcs = Seq( + arc(eventTransition.getOrElse(missingEventTransition(eventName)), preconditionPlace, 1), + arc(preconditionPlace, interactionTransition, 1) + ) + + (arcs, notProvidedError) + } + + // the (possible) event output arcs / places + private def buildInteractionOutputArcs(interaction: InteractionTransition, + eventTransitions: Seq[EventTransition]): Seq[Arc] = { + val resultPlace = createPlace(label = interaction.label, placeType = InteractionEventOutputPlace) + if (interaction.eventsToFire.nonEmpty) { + val eventArcs = interaction.eventsToFire.flatMap { event: EventDescriptor => + //Get the correct event transition + val eventTransition = eventTransitions.find(_.event.name == event.name).get + //Decide if there are multiple interactions that fire this transition, + // if so create a event combiner place + // else link the transition to the event. + val eventTransitionCount = eventTransitions.count(e => e.event.name == event.name) + if(eventTransitionCount > 1) { + //Create a new intermediate event place + val eventCombinerPlace: Place = createPlace(label = event.name, placeType = IntermediatePlace) + //Create a new intermediate event transition + val interactionToEventTransition: IntermediateTransition = IntermediateTransition(s"${interaction.interactionName}:${event.name}") + //link the interaction output place to the intermediate transition + val interactionOutputPlaceToIntermediateTransition: Arc = arc(resultPlace, interactionToEventTransition, 1, Some(event.name)) + //link the intermediate transition to the intermediate input place + val intermediateTransitionToEventCombinerPlace: Arc = arc(interactionToEventTransition, eventCombinerPlace, 1) + //Link the intermediate place to the event place + val eventCombinerPlaceToEventTransition = arc(eventCombinerPlace, eventTransition, 1) + Seq(intermediateTransitionToEventCombinerPlace, interactionOutputPlaceToIntermediateTransition, eventCombinerPlaceToEventTransition) + } + else { + val internalEventTransition = eventTransition + Seq(arc(resultPlace, internalEventTransition, 1, Some(event.name))) + } + } + arc(interaction, resultPlace, 1) +: eventArcs + } + else Seq.empty + } + + + /** + * Draws an arc from all required ingredients for an interaction + * If the ingredient has multiple consumers create a multi transition place and create both arcs for it + */ + private def buildInteractionInputArcs(t: InteractionTransition, + multipleConsumerFacilitatorTransitions: Seq[Transition], + ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]]): Seq[Arc] = { + + val (fieldNamesWithPrefixMulti, fieldNamesWithoutPrefix) = + t.nonProvidedIngredients.map(_.name).partition(ingredientsWithMultipleConsumers.contains) + + // the extra arcs to model multiple output transitions from one place + val internalDataInputArcs: Seq[Arc] = fieldNamesWithPrefixMulti flatMap { fieldName => + val multiTransitionPlace = createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace) + Seq( + // one arc from multiplier place to the transition + arc(getMultiTransition(fieldName, multipleConsumerFacilitatorTransitions), + multiTransitionPlace, + 1), + // one arc from extra added place to transition + arc(multiTransitionPlace, t, 1)) + } + + + // the data input arcs / places + val dataInputArcs: Seq[Arc] = fieldNamesWithoutPrefix.map(fieldName => arc(createPlace(fieldName, IngredientPlace), t, 1)) + + val dataOutputArcs: Seq[Arc] = + if(t.isReprovider) + fieldNamesWithoutPrefix.map(fieldName => arc(t, createPlace(fieldName, IngredientPlace), 1)) ++ + fieldNamesWithPrefixMulti.map(fieldName => arc(t, createPlace(s"${t.label}-$fieldName", placeType = MultiTransitionPlace), 1)) + else + Seq.empty + + val limitInteractionCountArc: Option[Arc] = + t.maximumInteractionCount.map(n => arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) + + dataInputArcs ++ dataOutputArcs ++ internalDataInputArcs ++ limitInteractionCountArc + } + + private def buildInteractionArcs(multipleOutputFacilitatorTransitions: Seq[Transition], + placeNameWithDuplicateTransitions: Map[String, Seq[InteractionTransition]], + eventTransitions: Seq[EventTransition]) + (t: InteractionTransition): Seq[Arc] = { + + val inputArcs: Seq[Arc] = buildInteractionInputArcs( + t, + multipleOutputFacilitatorTransitions, + placeNameWithDuplicateTransitions) + + val outputArcs: Seq[Arc] = buildInteractionOutputArcs(t, eventTransitions) + + inputArcs ++ outputArcs + } + + /** + * Compile the given recipe to a technical recipe that is useful for Baker. + * + * @param recipe ; The Recipe to compile and execute + * @param validationSettings The validation settings to follow for the validation + * @return + */ + def compileRecipe(recipe: Recipe, + validationSettings: ValidationSettings): CompiledRecipe = { + + def convertCheckpointEventToInteraction(e: CheckPointEvent) = + Interaction( + name = s"${checkpointEventInteractionPrefix}${e.name}", + inputIngredients = Seq.empty, + output = Seq(Event(e.name)), + requiredEvents = e.requiredEvents, + requiredOneOfEvents = e.requiredOneOfEvents) + + def convertSieveToInteraction(s: Sieve) = + Interaction( + name = s"${sieveInteractionPrefix}${s.name}", + inputIngredients = s.inputIngredients, + output = s.output, + requiredEvents = Set.empty, + requiredOneOfEvents = Set.empty + ) + + def flattenSubRecipesToInteraction(recipe: Recipe): Set[InteractionDescriptor] = { + def copyInteraction(i: InteractionDescriptor) = Interaction( + name = s"${subRecipePrefix}${recipe.name}$$${i.name}", + inputIngredients = i.inputIngredients, + output = i.output, + requiredEvents = i.requiredEvents, + requiredOneOfEvents = i.requiredOneOfEvents, + predefinedIngredients = i.predefinedIngredients, + overriddenIngredientNames = i.overriddenIngredientNames, + overriddenOutputIngredientName = i.overriddenOutputIngredientName, + maximumInteractionCount = i.maximumInteractionCount, + failureStrategy = i.failureStrategy, + eventOutputTransformers = i.eventOutputTransformers, + isReprovider = i.isReprovider, + oldName = Option(i.originalName) + ) + recipe.interactions.map(copyInteraction).toSet ++ + recipe.sieves.map(convertSieveToInteraction) ++ + recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ + recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) + } + + def flattenSensoryEvents(recipe: Recipe): Set[com.ing.baker.recipe.common.Event] = { + recipe.sensoryEvents ++ recipe.subRecipes.flatMap(flattenSensoryEvents) + } + + val precompileErrors: Seq[String] = Assertions.preCompileAssertions(recipe) + + // Extend the interactions with the checkpoint event interactions and sub-recipes + val actionDescriptors: Seq[InteractionDescriptor] = recipe.interactions ++ + recipe.checkpointEvents.map(convertCheckpointEventToInteraction) ++ + recipe.sieves.map(convertSieveToInteraction) ++ + recipe.subRecipes.flatMap(flattenSubRecipesToInteraction) + + // Flatten all sensory events from sub recipes + val sensoryEvents = flattenSensoryEvents(recipe) + + //All ingredient names provided by sensory events or by interactions + val allIngredientNames: Set[String] = + sensoryEvents.flatMap(e => e.providedIngredients.map(i => i.name)) ++ + actionDescriptors.flatMap(i => i.output.flatMap { e => + // check if the event was renamed (check if there is a transformer for this event) + i.eventOutputTransformers.get(e) match { + case Some(transformer) => e.providedIngredients.map(ingredient => transformer.ingredientRenames.getOrElse(ingredient.name, ingredient.name)) + case None => e.providedIngredients.map(_.name) + } + } + ) + + // For inputs for which no matching output cannot be found, we do not want to generate a place. + // It should be provided at runtime from outside the active petri net (marking) + val interactionTransitions = actionDescriptors.map(_.toInteractionTransition(recipe.defaultFailureStrategy, allIngredientNames)) + + val allInteractionTransitions: Seq[InteractionTransition] = interactionTransitions + + // events provided from outside + val sensoryEventTransitions: Seq[EventTransition] = sensoryEvents.map { + event => EventTransition(eventToCompiledEvent(event), isSensoryEvent = true, event.maxFiringLimit) + }.toIndexedSeq + + // events provided by other transitions / actions + val interactionEventTransitions: Seq[EventTransition] = allInteractionTransitions.flatMap { t => + t.eventsToFire.map(event => EventTransition(event, isSensoryEvent = false)) + } + + val allEventTransitions: Seq[EventTransition] = sensoryEventTransitions ++ interactionEventTransitions + + // Given the event classes, it is creating the ingredient places and + // connecting a transition to a ingredient place. + val internalEventArcs: Seq[Arc] = allInteractionTransitions.flatMap { t => + t.eventsToFire.flatMap { event => + event.ingredients.map { ingredient => + val from = interactionEventTransitions.find(_.label == event.name).get + arc(from, createPlace(ingredient.name, IngredientPlace), 1) + } + } + } + + //Create event limiter places so that events can only fire x amount of times. + val eventLimiterArcs: Seq[Arc] = sensoryEventTransitions.flatMap( + t => t.maxFiringLimit match { + case Some(n) => Seq(arc(createPlace(s"limit:${t.label}", FiringLimiterPlace(n)), t, 1)) + case None => Seq.empty + } + ) + + def findEventTransitionByEventName(eventName: String) = allEventTransitions.find(_.event.name == eventName) + + def findInteractionByLabel(label: String) = allInteractionTransitions.find(_.label == label) + + // This generates precondition arcs for Required Events (AND). + val (eventPreconditionArcs, preconditionANDErrors) = actionDescriptors.map { t => + buildEventAndPreconditionArcs(t, + findEventTransitionByEventName, + findInteractionByLabel) + }.unzipFlatten + + // This generates precondition arcs for Required Events (OR). + val (eventOrPreconditionArcs, preconditionORErrors) = actionDescriptors.map { t => + buildEventORPreconditionArcs(t, + findEventTransitionByEventName, + findInteractionByLabel) + }.unzipFlatten + + val (sensoryEventWithoutIngredients, sensoryEventWithIngredients) = sensoryEventTransitions.partition(_.event.ingredients.isEmpty) + + // It connects a sensory event to an ingredient places + val sensoryEventArcs: Seq[Arc] = sensoryEventWithIngredients + .flatMap(et => et.event.ingredients.map(ingredient => arc(et, createPlace(ingredient.name, IngredientPlace), 1))) + + val eventThatArePreconditions: Seq[String] = + actionDescriptors.flatMap { + actionDescriptor => actionDescriptor.requiredEvents ++ actionDescriptor.requiredOneOfEvents.flatten + } + + // It connects a sensory event to a dummy ingredient so it can be modelled into the Petri net + val sensoryEventArcsNoIngredientsArcs: Seq[Arc] = sensoryEventWithoutIngredients + //Filter out events that are preconditions to interactions + .filterNot(sensoryEvent => eventThatArePreconditions.contains(sensoryEvent.label)) + .map(sensoryEvent => arc(sensoryEvent, createPlace(sensoryEvent.label, EmptyEventIngredientPlace), 1)) + + // First find the cases where multiple transitions depend on the same ingredient place + val ingredientsWithMultipleConsumers: Map[String, Seq[InteractionTransition]] = + getIngredientsWithMultipleConsumers(allInteractionTransitions) + + // Add one new transition for each duplicate input (the newly added one in the image above) + val multipleConsumerFacilitatorTransitions: Seq[Transition] = + ingredientsWithMultipleConsumers.keys + .map(name => MultiFacilitatorTransition(label = name)) + .toIndexedSeq + + val multipleOutputFacilitatorArcs: Seq[Arc] = + multipleConsumerFacilitatorTransitions.map(t => + arc(createPlace(t.label, IngredientPlace), t, 1)) + + val interactionArcs: Seq[Arc] = + allInteractionTransitions.flatMap( + buildInteractionArcs( + multipleConsumerFacilitatorTransitions, + ingredientsWithMultipleConsumers, + interactionEventTransitions)) + + val arcs = (interactionArcs + ++ eventPreconditionArcs + ++ eventOrPreconditionArcs + ++ eventLimiterArcs + ++ sensoryEventArcs + ++ sensoryEventArcsNoIngredientsArcs + ++ internalEventArcs + ++ multipleOutputFacilitatorArcs) + + val petriNet: PetriNet = new PetriNet(Graph(arcs: _*)) + + val initialMarking: Marking[Place] = petriNet.places.collect { + case p @ Place(_, FiringLimiterPlace(n)) => p -> Map[Any, Int]((null, n)) + }.toMarking + + val errors = preconditionORErrors ++ preconditionANDErrors ++ precompileErrors + + val oldRecipeIdVariant : OldRecipeIdVariant = + recipe match { + case _: javadsl.Recipe => Scala212CompatibleJava + case _: kotlindsl.Recipe => Scala212CompatibleKotlin + case _ => Scala212CompatibleScala + } + + val compiledRecipe = CompiledRecipe.build( + name = recipe.name, + petriNet = petriNet, + initialMarking = initialMarking, + validationErrors = errors, + eventReceivePeriod = recipe.eventReceivePeriod, + retentionPeriod = recipe.retentionPeriod, + oldRecipeIdVariant = oldRecipeIdVariant, + ) + + postCompileValidations(compiledRecipe, validationSettings) + } + + def compileRecipe(recipe: Recipe): CompiledRecipe = compileRecipe(recipe, ValidationSettings.defaultValidationSettings) + + private def getMultiTransition(internalRepresentationName: String, + transitions: Seq[Transition]): Transition = { + transitions + .find(t => t.label.equals(internalRepresentationName)) + .getOrElse(throw new NoSuchElementException(s"No multi transition found with name $internalRepresentationName")) + } + + /** + * Obtains a map of each input place name that is used multiple times and the reflected transitions using it. + * + * @param actionTransitions Seq of reflected transition. + * @return A map from input place name to reflected transitions (where the transitions have as input the place). + */ + private def getIngredientsWithMultipleConsumers(actionTransitions: Seq[InteractionTransition]): Map[String, Seq[InteractionTransition]] = { + // Obtain a list of field name with their transition + val placeNameWithTransition: Seq[(String, InteractionTransition)] = for { + transition <- actionTransitions + inputPlaceName <- transition.nonProvidedIngredients.map(_.name) + } yield (inputPlaceName, transition) + + // Then obtain the places with multiple out-adjacent transitions + val ingredientsWithMultipleConsumers = placeNameWithTransition.groupBy { + case (placeName, _) => placeName + } // Group by place name + .filter { case (_, interactions) => interactions.size >= 2 } // Only keep those place names which have more than one out-adjacent transition + .map { case (placeName, interactions) => (placeName, interactions.map(_._2)) } // Cleanup the data structure + + ingredientsWithMultipleConsumers + } + + private def createPlace(label: String, placeType: PlaceType): Place = Place(label = s"${placeType.labelPrepend}$label", placeType) +} diff --git a/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/package.scala b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/package.scala new file mode 100644 index 000000000..04ea1f29f --- /dev/null +++ b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/package.scala @@ -0,0 +1,129 @@ +package com.ing.baker + +import com.ing.baker.il._ +import com.ing.baker.il.failurestrategy.InteractionFailureStrategy +import com.ing.baker.il.petrinet._ +import com.ing.baker.recipe.common +import com.ing.baker.recipe.common.InteractionDescriptor +import com.ing.baker.types._ + +import scala.collection.immutable.Seq + +package object compiler { + + def ingredientToCompiledIngredient(ingredient: common.Ingredient): IngredientDescriptor = IngredientDescriptor(ingredient.name, ingredient.ingredientType) + + def eventToCompiledEvent(event: common.Event): EventDescriptor = EventDescriptor(event.name, event.providedIngredients.map(ingredientToCompiledIngredient)) + + implicit class InteractionOps(interaction: InteractionDescriptor) { + + def toInteractionTransition(defaultFailureStrategy: common.InteractionFailureStrategy, allIngredientNames: Set[String]): InteractionTransition = + interactionTransitionOf(interaction, defaultFailureStrategy, allIngredientNames) + + def interactionTransitionOf(interactionDescriptor: InteractionDescriptor, + defaultFailureStrategy: common.InteractionFailureStrategy, + allIngredientNames: Set[String]): InteractionTransition = { + + //This transforms the event using the eventOutputTransformer to the new event + //If there is no eventOutputTransformer for the event the original event is returned + def transformEventType(event: common.Event): common.Event = + interactionDescriptor.eventOutputTransformers.get(event) + match { + case Some(eventOutputTransformer) => + new common.Event { + override val name: String = eventOutputTransformer.newEventName + override val providedIngredients: Seq[common.Ingredient] = event.providedIngredients.map(i => + new common.Ingredient(eventOutputTransformer.ingredientRenames.getOrElse(i.name, i.name), i.ingredientType)) + } + case _ => event + } + + def transformEventOutputTransformer(recipeEventOutputTransformer: common.EventOutputTransformer): EventOutputTransformer = + EventOutputTransformer(recipeEventOutputTransformer.newEventName, recipeEventOutputTransformer.ingredientRenames) + + def transformEventToCompiledEvent(event: common.Event): EventDescriptor = { + EventDescriptor( + event.name, + event.providedIngredients.map(ingredientToCompiledIngredient)) + } + + // Replace RecipeInstanceId to recipeInstanceIdName tag as know in compiledRecipe + // Replace BakerMetaData to BakerMetaData tag as know in compiledRecipe + // Replace BakerEventList to BakerEventList tag as know in compiledRecipe + // Replace ingredient tags with overridden tags + val inputFields: Seq[(String, Type)] = interactionDescriptor.inputIngredients + .map { ingredient => + if (ingredient.name == common.recipeInstanceIdName) il.recipeInstanceIdName -> ingredient.ingredientType + else if(ingredient.name == common.recipeInstanceMetadataName) il.recipeInstanceMetadataName -> ingredient.ingredientType + else if(ingredient.name == common.recipeInstanceEventListName) il.recipeInstanceEventListName -> ingredient.ingredientType + else interactionDescriptor.overriddenIngredientNames.getOrElse(ingredient.name, ingredient.name) -> ingredient.ingredientType + } + + val (originalEvents, eventsToFire): (Seq[EventDescriptor], Seq[EventDescriptor]) = { + val originalCompiledEvents = interactionDescriptor.output.map(transformEventToCompiledEvent) + val compiledEvents = interactionDescriptor.output.map(transformEventType).map(transformEventToCompiledEvent) + (originalCompiledEvents, compiledEvents) + } + + //For each ingredient that is not provided + //And is of the type Optional or Option + //Add it to the predefinedIngredients List as empty + //Add the predefinedIngredients later to overwrite any created empty field with the given predefined value. + val predefinedIngredientsWithOptionalsEmpty: Map[String, Value] = + inputFields.flatMap { + case (name, types.OptionType(_)) if !allIngredientNames.contains(name) => Seq(name -> NullValue) + case _ => Seq.empty + }.toMap ++ interactionDescriptor.predefinedIngredients + + val (failureStrategy: InteractionFailureStrategy, exhaustedRetryEvent: Option[EventDescriptor], functionalRetryEvent: Option[EventDescriptor]) = { + interactionDescriptor.failureStrategy.getOrElse[common.InteractionFailureStrategy](defaultFailureStrategy) match { + case common.InteractionFailureStrategy.RetryWithIncrementalBackoff(initialTimeout, backoffFactor, maximumRetries, maxTimeBetweenRetries, fireRetryExhaustedEvent, fireFunctionalEvent) => + val exhaustedRetryEvent: Option[EventDescriptor] = fireRetryExhaustedEvent match { + case Some(None) => Some(EventDescriptor(interactionDescriptor.name + exhaustedEventAppend, Seq.empty)) + case Some(Some(eventName)) => Some(EventDescriptor(eventName, Seq.empty)) + case None => None + } + val functionalFailedEvent: Option[EventDescriptor] = fireFunctionalEvent match { + case Some(None) => Some(EventDescriptor(interactionDescriptor.name + functionalFailedEventAppend, Seq.empty)) + case Some(Some(eventName)) => Some(EventDescriptor(eventName, Seq.empty)) + case None => None + } + + (il.failurestrategy.RetryWithIncrementalBackoff(initialTimeout, backoffFactor, maximumRetries, maxTimeBetweenRetries, exhaustedRetryEvent, functionalFailedEvent),exhaustedRetryEvent, functionalFailedEvent) + + case common.InteractionFailureStrategy.BlockInteraction() => (il.failurestrategy.BlockInteraction, None, None) + + case common.InteractionFailureStrategy.FireEventAfterFailure(eventNameOption) => + val eventName = eventNameOption.getOrElse(interactionDescriptor.name + exhaustedEventAppend) + val exhaustedRetryEvent: EventDescriptor = EventDescriptor(eventName, Seq.empty) + (il.failurestrategy.FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), None) + + case common.InteractionFailureStrategy.FireEventAndBlock(eventNameOption) => + val eventName = eventNameOption.getOrElse(interactionDescriptor.name + exhaustedEventAppend) + val exhaustedRetryEvent: EventDescriptor = EventDescriptor(eventName, Seq.empty) + (il.failurestrategy.FireEventAfterFailure(exhaustedRetryEvent), Some(exhaustedRetryEvent), None) + + case common.InteractionFailureStrategy.FireEventAndResolve(eventNameOption) => + val eventName = eventNameOption.getOrElse(interactionDescriptor.name + functionalFailedEventAppend) + val functionalFailed: EventDescriptor = EventDescriptor(eventName, Seq.empty) + (il.failurestrategy.FireFunctionalEventAfterFailure(functionalFailed), None, Some(functionalFailed)) + + case _ => (il.failurestrategy.BlockInteraction, None, None) + } + } + + InteractionTransition( + eventsToFire = eventsToFire ++ exhaustedRetryEvent ++ functionalRetryEvent, + originalEvents = originalEvents ++ exhaustedRetryEvent ++ functionalRetryEvent, + requiredIngredients = inputFields.map { case (name, ingredientType) => IngredientDescriptor(name, ingredientType) }, + interactionName = interactionDescriptor.name, + originalInteractionName = interactionDescriptor.originalName, + predefinedParameters = predefinedIngredientsWithOptionalsEmpty, + maximumInteractionCount = interactionDescriptor.maximumInteractionCount, + failureStrategy = failureStrategy, + eventOutputTransformers = interactionDescriptor.eventOutputTransformers.map { + case (event, transformer) => event.name -> transformEventOutputTransformer(transformer) }, + isReprovider = interactionDescriptor.isReprovider) + } + } +} diff --git a/core/baker-compiler-scala/src/test/java/RecipeCompilerTests.java b/core/baker-compiler-scala/src/test/java/RecipeCompilerTests.java new file mode 100644 index 000000000..ccb0ff520 --- /dev/null +++ b/core/baker-compiler-scala/src/test/java/RecipeCompilerTests.java @@ -0,0 +1,224 @@ +import com.ing.baker.compiler.RecipeCompilerScala; +import com.ing.baker.il.CompiledRecipe; +import com.ing.baker.il.EventDescriptor; +import com.ing.baker.il.petrinet.InteractionTransition; +import com.ing.baker.recipe.annotations.FiresEvent; +import com.ing.baker.recipe.annotations.RequiresIngredient; +import com.ing.baker.recipe.javadsl.CheckPointEvent; +import com.ing.baker.recipe.javadsl.Interaction; +import com.ing.baker.recipe.javadsl.InteractionDescriptor; +import com.ing.baker.recipe.javadsl.Recipe; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class RecipeCompilerTests { + + static class EventA { + } + + static class EventB { + } + + static class EventC { + } + + static class EventD { + } + + static class EventE { + } + + public interface InteractionA extends Interaction { + + interface ReserveItemsOutcome { + } + + class OrderHadUnavailableItems implements ReserveItemsOutcome { + + public final List unavailableItems; + + public OrderHadUnavailableItems(List unavailableItems) { + this.unavailableItems = unavailableItems; + } + } + + class ItemsReserved implements ReserveItemsOutcome { + + public final List reservedItems; + + public ItemsReserved(List reservedItems) { + this.reservedItems = reservedItems; + } + } + + @FiresEvent(oneOf = {ItemsReserved.class, OrderHadUnavailableItems.class}) + ReserveItemsOutcome apply( + @RequiresIngredient("orderId") String id, + @RequiresIngredient("items") List items + ); + } + + public interface InteractionB extends Interaction { + + interface ReserveItemsOutcome { + } + + class OrderHadUnavailableItems implements ReserveItemsOutcome { + + public final List unavailableItems; + + public OrderHadUnavailableItems(List unavailableItems) { + this.unavailableItems = unavailableItems; + } + } + + class ItemsReserved implements ReserveItemsOutcome { + + public final List reservedItems; + + public ItemsReserved(List reservedItems) { + this.reservedItems = reservedItems; + } + } + + @FiresEvent(oneOf = {ItemsReserved.class, OrderHadUnavailableItems.class}) + ReserveItemsOutcome apply( + @RequiresIngredient("orderId") String id, + @RequiresIngredient("items") List items + ); + } + + interface ReserveItemsOutcome { + } + + @Test + public void shouldCompileSimpleRecipe() { + Recipe recipe = new Recipe("recipe") + .withSensoryEvents(EventA.class); + CompiledRecipe compiled = RecipeCompilerScala.compileRecipe(recipe); + + Assertions.assertEquals("8682aa9310e38509", compiled.recipeId()); + + } + + @Test + public void shouldCompileSimpleRecipeWithInteraction() { + + Recipe recipe = new Recipe("recipe") + .withSensoryEvents(EventA.class) + .withInteraction(InteractionDescriptor.of(InteractionA.class)); + CompiledRecipe compiled = RecipeCompilerScala.compileRecipe(recipe); + + Assertions.assertEquals("796a3cb3eb68b35d", compiled.recipeId()); + + } + + @Test + public void shouldFailOnDuplicatedEventsEvenWithFiringLimitDifference() { + // "Duplicated" EventA + // Because Set backing sensoryEvents has <=4 elements (is a specialized Set.Set4) it only uses `equals` in `contains` + Recipe recipe1 = new Recipe("recipe1") + .withSensoryEvents(EventA.class, EventB.class, EventC.class, EventD.class) + .withSensoryEventsNoFiringLimit(EventA.class); + + // "Duplicated" EventA + // Because Set backing sensoryEvents has >4 elements (is a HashSet) and uses `hashcode` in `contains` + Recipe recipe2 = new Recipe("recipe2") + .withSensoryEvents(EventA.class, EventB.class, EventC.class, EventD.class, EventE.class) + .withSensoryEventsNoFiringLimit(EventA.class); + + Assertions.assertThrows(IllegalStateException.class, () -> + RecipeCompilerScala.compileRecipe(recipe1)); + + Assertions.assertThrows(IllegalStateException.class, () -> + RecipeCompilerScala.compileRecipe(recipe2)); + } + + @Test + public void shouldAddInteractionsForCheckpointEvents() { + + Recipe recipe = new Recipe("recipe1") + .withSensoryEvents(EventB.class, EventC.class, EventD.class) + .withSensoryEventsNoFiringLimit(EventA.class) + .withCheckpointEvent(new CheckPointEvent("Success") + .withRequiredEvents(EventB.class, EventC.class)); + + + CompiledRecipe compiled = RecipeCompilerScala.compileRecipe(recipe); + + Object actual = convertList(compiled.petriNet().transitions()) + .stream() + .filter(InteractionTransition.class::isInstance) + .map(i -> ((InteractionTransition) i).interactionName()) + .collect(Collectors.toUnmodifiableList()); + + Assertions.assertEquals(java.util.List.of("$CheckpointEventInteraction$Success"), actual); + + } + + class SubSubSubEvent { + } + + class SubSubEvent { + } + + class SubEvent { + } + + class Event { + } + + @Test + public void shouldAddSubRecipe() { + // Several layers deep to verify if everything propagates correctly + Recipe subSubSubRecipe = new Recipe("subSubSubRecipe") + .withSensoryEvent(SubSubSubEvent.class) + .withCheckpointEvent(new CheckPointEvent("subSubSubCheckpointEvent")) + .withInteraction(InteractionDescriptor.of(InteractionA.class)); + + Recipe subSubRecipe = new Recipe("SubSubRecipe") + .withSensoryEvent(SubSubEvent.class) + .withSubRecipe(subSubSubRecipe) + .withCheckpointEvent(new CheckPointEvent("subSubCheckpointEvent")) + .withInteraction(InteractionDescriptor.of(InteractionB.class)); + + Recipe subRecipe = new Recipe("SubRecipe") + .withSensoryEvent(SubEvent.class) + .withSubRecipe(subSubRecipe) + .withCheckpointEvent(new CheckPointEvent("subCheckpointEvent")) + .withInteraction(InteractionDescriptor.of(InteractionA.class)); + + Recipe recipe = new Recipe("recipe") + .withSensoryEvent(Event.class) + .withSubRecipe(subRecipe) + .withCheckpointEvent(new CheckPointEvent("checkpointEvent")); + + + CompiledRecipe compiled = RecipeCompilerScala.compileRecipe(recipe); + + Object actualSensoryEvents = convertList(compiled.sensoryEvents()).stream().map(i -> ((EventDescriptor) i).name()).collect(Collectors.toUnmodifiableList()); + List expectedSensoryEvents = java.util.List.of("SubSubEvent", "SubEvent", "SubSubSubEvent", "Event"); + + Assertions.assertEquals(expectedSensoryEvents, actualSensoryEvents); + + Object actual = convertList(compiled.petriNet().transitions()) + .stream() + .filter(InteractionTransition.class::isInstance) + .map(i -> ((InteractionTransition) i).interactionName()) + .collect(Collectors.toUnmodifiableList()); + + List expected = java.util.List.of("$CheckpointEventInteraction$subSubSubCheckpointEvent", "$SubRecipe$SubRecipe$InteractionA", "$CheckpointEventInteraction$subSubCheckpointEvent", "$CheckpointEventInteraction$subCheckpointEvent", "$CheckpointEventInteraction$checkpointEvent", "$SubRecipe$subSubSubRecipe$InteractionA", "$SubRecipe$SubSubRecipe$InteractionB"); + + Assertions.assertEquals(expected, actual); + } + + private java.util.List convertList(scala.collection.immutable.Set list) { + java.util.List l = new ArrayList(); + list.foreach(i -> l.add(i)); + return l; + } +} diff --git a/core/baker-compiler-scala/src/test/kotlin/RecipeCompilerTest.kt b/core/baker-compiler-scala/src/test/kotlin/RecipeCompilerTest.kt new file mode 100644 index 000000000..ad3aa84ad --- /dev/null +++ b/core/baker-compiler-scala/src/test/kotlin/RecipeCompilerTest.kt @@ -0,0 +1,55 @@ +package com.ing.baker.compiler + +import com.ing.baker.recipe.javadsl.Interaction +import com.ing.baker.recipe.kotlindsl.recipe +import org.junit.Assert.assertEquals +import org.junit.Test + +class RecipeCompilerTest { + + class EventA + + interface InteractionA : Interaction { + sealed interface ReserveItemsOutcome + class ItemsReserved(val reservedItems: List) : ReserveItemsOutcome + class OrderHadUnavailableItems(val unavailableItems: List) : ReserveItemsOutcome + + fun apply( + orderId: String, + items: List + ): ReserveItemsOutcome + } + + @Test + fun `should compile dsl to recipe`() { + + val recipe = recipe("recipe") { + sensoryEvents { + event() + } + interaction() + } + + val compiled = RecipeCompilerScala.compileRecipe(recipe) + + assertEquals("796a3cb3eb68b35d", compiled.recipeId()) + } + + + @Test + fun `should compile dsl to recipe ingredient`() { + + val recipe = recipe("recipe") { + sensoryEvents { + event() + } + interaction() + ingredient("extract") @JvmSerializableLambda {"Hello123"} + } + + val compiled = RecipeCompilerScala.compileRecipe(recipe) + + assertEquals("ec448bcd08163a73", compiled.recipeId()) + } + +} \ No newline at end of file diff --git a/core/baker-compiler-scala/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala b/core/baker-compiler-scala/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala new file mode 100644 index 000000000..010182834 --- /dev/null +++ b/core/baker-compiler-scala/src/test/scala/com/ing/baker/compiler/RecipeCompilerSpec.scala @@ -0,0 +1,633 @@ +package com.ing.baker.compiler + +import com.ing.baker.il.petrinet.InteractionTransition + +import java.util.Optional +import com.ing.baker.il.{CompiledRecipe, RecipeVisualStyle, RecipeVisualizer, ValidationSettings, checkpointEventInteractionPrefix, subRecipePrefix} +import com.ing.baker.recipe.TestRecipe._ +import com.ing.baker.recipe.{TestRecipeJava, common} +import com.ing.baker.recipe.common.InteractionFailureStrategy +import com.ing.baker.recipe.common.InteractionFailureStrategy.RetryWithIncrementalBackoff.UntilDeadline +import com.ing.baker.recipe.scaladsl._ +import com.ing.baker.types._ +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike + +import scala.concurrent.duration._ +import scala.language.postfixOps + +class RecipeCompilerSpec extends AnyWordSpecLike with Matchers { + + "The recipe compiler" should { + + "not have validation errors for a valid recipe" in { + val recipe: Recipe = getRecipe("ValidRecipe") + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe List.empty + + // dumpToFile("TestRecipe.svg", compiledRecipe.getVisualRecipeAsSVG) + } + + "add the exhausted retry event to the interaction event output list if defined with FireEventAfterFailure (with name)" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.FireEventAfterFailure(Some(exhaustedEvent.name)))) + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with FireEventAfterFailure (no name)" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.FireEventAfterFailure(None))) + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") + } + + "add the exhausted retry event to the interaction event output list if defined with FireEventAndBlock (with name)" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.FireEventAndBlock(Some(exhaustedEvent.name)))) + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with FireEventAndBlock (no name)" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.FireEventAndBlock(None))) + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") + } + + "add the exhausted retry event to the interaction event output list if defined with FireEventAndResolve (with name)" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.FireEventAndResolve(Some(exhaustedEvent.name)))) + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with FireEventAndResolve (no name)" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.FireEventAndResolve(None))) + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.allEvents.map(_.name) should contain("InteractionOneFunctionalFailed") + } + + "add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireRetryExhaustedEvent(exhaustedEvent) + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent 2" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireRetryExhaustedEvent(Some("RetryExhausted")) + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with withFireRetryExhaustedEvent 3" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireRetryExhaustedEvent() + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") + } + + "add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock" in { + val exhaustedEvent = Event("RetryExhausted") + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireEventAndBlock(exhaustedEvent) + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock 2" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireEventAndBlock(Some("RetryExhausted")) + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with withFireEventAndBlock 3" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireEventAndBlock() + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain("InteractionOneRetryExhausted") + } + + "add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve" in { + val exhaustedEvent = Event("RetryExhausted") + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireEventAndResolve(exhaustedEvent) + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve 2" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireEventAndResolve(Some("RetryExhausted")) + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain(exhaustedEvent.name) + } + + "add the exhausted retry event to the interaction event output list if defined with withFireEventAndResolve 3" in { + val recipe = Recipe("RetryExhaustedRecipe") + .withSensoryEvent(initialEvent) + .withInteractions(interactionOne.withFailureStrategy( + InteractionFailureStrategy.RetryWithIncrementalBackoff.builder() + .withInitialDelay(10 milliseconds) + .withUntil(Some(UntilDeadline(10 seconds))) + .withFireEventAndResolve() + .build())) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + compiledRecipe.allEvents.map(_.name) should contain("InteractionOneFunctionalFailed") + } + + "generate the same id for the same recipe" in { + val first = RecipeCompilerScala.compileRecipe(getRecipe("ValidRecipe")).recipeId + (1 to 10) + .map(_ => getRecipe("ValidRecipe")) + .map(RecipeCompilerScala.compileRecipe(_).recipeId) + .foreach(_ shouldBe first) + } + + "generate different ids for recipes with changes on transitions other than the name" in { + val input = Ingredient[Int]("ingredient") + val output = Event("event", Seq.empty, None) + val interaction = Interaction("interaction", Seq(input), Seq(output)) + val name = "RecipeName" + val recipe1 = Recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name -> 1)) + val recipe2 = Recipe(name).withInteraction(interaction.withPredefinedIngredients(input.name -> 2)) + RecipeCompilerScala.compileRecipe(recipe1).recipeId shouldNot be(RecipeCompilerScala.compileRecipe(recipe2).recipeId) + } + + "give a list of missing ingredients if an interaction has an ingredient that is not provided by any other event or interaction" in { + val recipe = Recipe("NonProvidedIngredient") + .withSensoryEvent(secondEvent) + .withInteractions(interactionOne) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors should contain("Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction") + } + + "give an error if the RecipeInstanceId is required and is not of the String type" in { + val wrongrecipeInstanceIdInteraction = + Interaction( + name = "wrongrecipeInstanceIdInteraction", + inputIngredients = Seq(new Ingredient[Int](common.recipeInstanceIdName), initialIngredient), + output = Seq.empty) + + val recipe = Recipe("NonProvidedIngredient") + .withSensoryEvent(initialEvent) + .withInteractions(wrongrecipeInstanceIdInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors should contain("Non supported process id type: Int32 on interaction: 'wrongrecipeInstanceIdInteraction'") + } + + "give an error if the MetaData is required and is not of the Map type" in { + val wrongMetaDataInteraction = + Interaction( + name = "wrongMetaDataInteraction", + inputIngredients = Seq(new Ingredient[Int](common.recipeInstanceMetadataName), initialIngredient), + output = Seq.empty) + + val recipe = Recipe("NonProvidedIngredient") + .withSensoryEvent(initialEvent) + .withInteractions(wrongMetaDataInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors should contain("Non supported MetaData type: Int32 on interaction: 'wrongMetaDataInteraction'") + } + + "give an error if the baker internal ingredients are provided" in { + val wrongDateEvent = Event("WrongDataEvent", + Seq( + Ingredient[String]("recipeInstanceId"), + Ingredient[String]("RecipeInstanceMetaData")), + maxFiringLimit = None) + + val wrongDateEvent2 = Event("WrongDataEvent2", + Seq(Ingredient[String]("RecipeInstanceEventList")), + maxFiringLimit = None) + + val wrongMetaDataInteraction = + Interaction( + name = "wrongDataProvidedInteraction", + inputIngredients = Seq(new Ingredient[String](common.recipeInstanceIdName), initialIngredient), + output = Seq(wrongDateEvent2)) + + val recipe = Recipe("WrongDataRecipe") + .withSensoryEvents(initialEvent, wrongDateEvent) + .withInteractions(wrongMetaDataInteraction) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe List( + "Ingredient 'recipeInstanceId' is provided and this is a reserved name for internal use in Baker", + "Ingredient 'RecipeInstanceMetaData' is provided and this is a reserved name for internal use in Baker", + "Ingredient 'RecipeInstanceEventList' is provided and this is a reserved name for internal use in Baker" + ) + } + + "give a list of wrong ingredients" when { + "an ingredient is of the wrong type" in { + val initialIngredientInt = new common.Ingredient("initialIngredient", RecordType(Seq(RecordField("data", Int32)))) + val initialEventInt = Event("InitialEvent", Seq(initialIngredientInt), None) + + val recipe = Recipe("WrongTypedIngredient") + .withInteractions( + interactionOne) + .withSensoryEvent(initialEventInt) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors should contain("Interaction 'InteractionOne' expects ingredient 'initialIngredient:CharArray', however incompatible type: 'Record(data: Int32)' was provided") + } + + "an optional ingredient is of the wrong Option type" in { + val initialIngredientOptionalInt = Ingredient[Optional[Int]]("initialIngredientOptionalInt") + val initialIngredientOptionalString = Ingredient[Optional[String]]("initialIngredientOptionalInt") + val initialIngredientOptionInt = Ingredient[Option[List[Int]]]("initialIngredientOptionInt") + val initialIngredientOptionString = Ingredient[Option[List[String]]]("initialIngredientOptionInt") + val initialEventIntOptional = Event("initialEventIntOptional", Seq(initialIngredientOptionalString), None) + val initialEventIntOption = Event("initialEventIntOption", Seq(initialIngredientOptionString), None) + val interactionOptional = + Interaction( + name = "InteractionWithOptional", + inputIngredients = Seq(recipeInstanceId, initialIngredientOptionalInt, initialIngredientOptionInt), + output = Seq.empty) + + val recipe = Recipe("WrongTypedOptionalIngredient") + .withInteractions( + interactionOptional) + .withSensoryEvents(initialEventIntOptional, initialEventIntOption) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors should contain("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionalInt:Option[Int32]', however incompatible type: 'Option[CharArray]' was provided") + compiledRecipe.validationErrors should contain("Interaction 'InteractionWithOptional' expects ingredient 'initialIngredientOptionInt:Option[List[Int32]]', however incompatible type: 'Option[List[CharArray]]' was provided") + } + } + + "give no errors if an Optional ingredient is of the correct Option type" in { + val initialIngredientInt = Ingredient[Optional[List[Int]]]("initialIngredient") + val initialEventInt = Event("InitialEvent", Seq(initialIngredientInt), None) + val interactionOptional = + Interaction( + name = "InteractionWithOptional", + inputIngredients = Seq(recipeInstanceId, initialIngredientInt), + output = Seq.empty) + + val recipe = Recipe("CorrectTypedOptionalIngredient") + .withInteractions( + interactionOptional) + .withSensoryEvent(initialEventInt) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe empty + } + + "give a list of wrong ingredients if a predefined ingredient is of the wrong type" in { + val recipe = Recipe("WrongGivenPredefinedIngredient") + .withInteractions( + interactionOne + .withRequiredEvent(initialEvent) + .withPredefinedIngredients(("initialIngredient", Integer.valueOf(12)))) + .withSensoryEvent(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors should contain("Predefined argument 'initialIngredient' is not of type: CharArray on interaction: 'InteractionOne'") + } + + "give a list of wrong ingredients if a predefined ingredient is not needed by the interaction" in { + val recipe = Recipe("WrongGivenIngredient") + .withInteractions( + interactionOne + .withPredefinedIngredients(("WrongIngredient", null))) + .withSensoryEvent(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors should contain("Predefined argument 'WrongIngredient' is not defined on interaction: 'InteractionOne'") + } + + "validate if unreachable interactions exist or not" in { + val recipe = Recipe("RecipeWithUnreachableInteraction") + .withInteractions(interactionSeven.withMaximumInteractionCount(1), interactionEight) + .withSensoryEvent(initialEvent) + + val compiledRecipe = RecipeCompilerScala.compileRecipe(recipe, + ValidationSettings(allowNonExecutableInteractions = false)) + + compiledRecipe.validationErrors should contain("InteractionEight is not executable") + } + + "fail compilation" when { + "the interaction name is null or empty" in { + List("", null) foreach { name => + val invalidInteraction = Interaction(name, Seq.empty, Seq()) + val recipe = Recipe("InteractionNameTest").withInteractions(invalidInteraction).withSensoryEvent(initialEvent) + + intercept[IllegalArgumentException](RecipeCompilerScala.compileRecipe(recipe)).getMessage.shouldBe("Interaction with a null or empty name found") + } + } + + + "the event name is null or empty" in { + List("", null) foreach { name => + val invalidEvent = Event(name) + val recipe = Recipe("EventNameTest").withSensoryEvent(invalidEvent).withInteraction(interactionOne) + + intercept[IllegalArgumentException](RecipeCompilerScala.compileRecipe(recipe)).getMessage.shouldBe("Event with a null or empty name found") + } + } + + "the ingredient name is null or empty" in { + List("", null) foreach { name => + val invalidIngredient = Ingredient[String](name) + val recipe = Recipe("IngredientNameTest").withSensoryEvent(Event("someEvent", invalidIngredient)).withInteraction(interactionOne) + + intercept[IllegalArgumentException](RecipeCompilerScala.compileRecipe(recipe)).getMessage.shouldBe("Ingredient with a null or empty name found") + } + } + + "the recipe name is null or empty" in { + List("", null) foreach { name => + val recipe = Recipe(name) + + intercept[IllegalArgumentException](RecipeCompilerScala.compileRecipe(recipe)).getMessage.shouldBe("Recipe with a null or empty name found") + } + } + + "an Interaction is reprovider, but has no required events" in { + val recipe: Recipe = Recipe("LoopingWithReprovider") + .withInteraction(interactionOne.isReprovider(true)) + .withSensoryEvents(initialEvent) + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe List("Reprovider interaction InteractionOne needs to have a event precondition") + } + } + + "give the interaction an optional ingredient as empty" when { + "the ingredient is not provided" in { + val recipe: Recipe = Recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients) + .withSensoryEvent(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe List.empty + compiledRecipe.interactionTransitions + .map(it => + if (it.interactionName.equals("OptionalIngredientInteraction")) { + it.predefinedParameters.size shouldBe 4 + it.predefinedParameters("missingJavaOptional") shouldBe NullValue + it.predefinedParameters("missingJavaOptional2") shouldBe NullValue + it.predefinedParameters("missingScalaOption") shouldBe NullValue + it.predefinedParameters("missingScalaOption2") shouldBe NullValue + }) + } + } + + "give the interaction an optional ingredient with value" when { + "the ingredient is provided" in { + val optionalProviderEvent = Event("optionalProviderEvent", missingJavaOptional) + + val recipe: Recipe = Recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients) + .withSensoryEvents(initialEvent, optionalProviderEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe List.empty + compiledRecipe.interactionTransitions + .map(it => + if (it.interactionName.equals("OptionalIngredientInteraction")) { + it.predefinedParameters.size shouldBe 3 + it.predefinedParameters("missingJavaOptional2") shouldBe NullValue + it.predefinedParameters("missingScalaOption") shouldBe NullValue + it.predefinedParameters("missingScalaOption2") shouldBe NullValue + }) + } + + "the ingredient is renamed and provided via an event" in { + val stringOptionIngredient = Ingredient[Option[String]]("stringOptionIngredient") + val renamedStringOptionIngredient = Ingredient[Option[String]]("renamedStringOptionIngredient") + + val eventWithOptionIngredient = Event("eventWithOptionIngredient", stringOptionIngredient) + + val interactionWithOptionIngredient = Interaction("interactionWithOptionIngredient", Seq(initialIngredient), Seq(eventWithOptionIngredient)) + + val secondInteraction = Interaction("secondInteraction", Seq(renamedStringOptionIngredient), Seq()) + + val recipe = Recipe("interactionWithEventOutputTransformer") + .withSensoryEvent(initialEvent) + .withInteraction(interactionWithOptionIngredient + .withEventOutputTransformer(eventWithOptionIngredient, "RenamedEventWithOptionIngredient", Map("stringOptionIngredient" -> "renamedStringOptionIngredient"))) + .withInteraction(secondInteraction) + + val compiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe empty + + val transition = compiledRecipe.interactionTransitions.find(_.interactionName == "secondInteraction").get + transition.nonProvidedIngredients.map(_.name) should contain("renamedStringOptionIngredient") + } + + "the ingredient is predefined" in { + val ingredientValue: Optional[String] = java.util.Optional.of("value") + val recipe: Recipe = Recipe("MissingOptionalRecipe") + .withInteraction( + interactionWithOptionalIngredients + .withPredefinedIngredients(("missingJavaOptional", ingredientValue)) + ) + .withSensoryEvents(initialEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe List.empty + compiledRecipe.interactionTransitions + .map(it => + if (it.interactionName.equals("OptionalIngredientInteraction")) { + it.predefinedParameters.size shouldBe 4 + it.predefinedParameters("missingJavaOptional") shouldBe PrimitiveValue("value") + it.predefinedParameters("missingJavaOptional2") shouldBe NullValue + it.predefinedParameters("missingScalaOption") shouldBe NullValue + it.predefinedParameters("missingScalaOption2") shouldBe NullValue + }) + } + + "the ingredient is provided, but not wrapped in an Option type" in { + val optionalProviderEvent = Event("optionalProviderEvent", missingJavaOptionalDirectString) + + val recipe: Recipe = Recipe("MissingOptionalRecipe") + .withInteraction(interactionWithOptionalIngredients) + .withSensoryEvents(initialEvent, optionalProviderEvent) + + val compiledRecipe: CompiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.validationErrors shouldBe List.empty + compiledRecipe.interactionTransitions + .map(it => + if (it.interactionName.equals("OptionalIngredientInteraction")) { + it.predefinedParameters.size shouldBe 3 + it.predefinedParameters("missingJavaOptional2") shouldBe NullValue + it.predefinedParameters("missingScalaOption") shouldBe NullValue + it.predefinedParameters("missingScalaOption2") shouldBe NullValue + }) + } + } + + "give the correct id" when { + "it compiles a java recipe" in { + val recipe = TestRecipeJava.getRecipe("id-test-recipe") + val compiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.recipeId shouldBe "220827c42a75b3f8" + } + } + + "give the interaction with Reprovider enabled" when { + "it compiles a java recipe and changes recipeId" in { + val recipe = TestRecipeJava.getRecipeReprovider("id-test-recipe") + val compiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.recipeId shouldBe "416e8abc02abcbee" + } + } + + "give the interaction for checkpoint-events" when { + "it compiles a java recipe" in { + val recipe = TestRecipeJava.getRecipe("id-test-recipe") + .withCheckpointEvent(CheckPointEvent("Success") + .withRequiredEvent(initialEvent)) + val compiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + compiledRecipe.recipeId shouldBe "469441173f91869a" + compiledRecipe.petriNet.transitions.count { case i: InteractionTransition => i.interactionName.contains(s"${checkpointEventInteractionPrefix}Success") case _ => false } shouldBe 1 + } + } + + "give the interaction for sub-recipes" when { + "it compiles a java recipe" in { + + val subSubRecipe: Recipe = Recipe("SubSubRecipe") + .withInteractions( + interactionOne + .withEventOutputTransformer(interactionOneSuccessful, Map("interactionOneOriginalIngredient" -> "interactionOneIngredient")) + .withFailureStrategy(InteractionFailureStrategy.RetryWithIncrementalBackoff(initialDelay = 10 millisecond, maximumRetries = 3)), + interactionTwo + .withOverriddenIngredientName("initialIngredientOld", "initialIngredient"), + ) + + val subRecipe: Recipe = Recipe("SubRecipe") + .withInteractions( + interactionThree + .withMaximumInteractionCount(1), + interactionFour + .withRequiredEvents(secondEvent, eventFromInteractionTwo), + interactionFive, + interactionSix, + ) + .withSubRecipe(subSubRecipe) + + val recipe: Recipe = Recipe("Recipe") + .withSensoryEvents( + initialEvent, + initialEventExtendedName, + secondEvent, + notUsedSensoryEvent + ) + .withInteractions( + providesNothingInteraction, + sieveInteraction + ) + .withSubRecipe(subRecipe) + + val compiledRecipe = RecipeCompilerScala.compileRecipe(recipe) + + val res = compiledRecipe.petriNet.transitions + .flatMap { + case i: InteractionTransition => List(i.interactionName) + case _ => List.empty + } + .filter( _.startsWith(subRecipePrefix)) + +// compiledRecipe.recipeId shouldBe "fb5346c571ca5a47" +// res shouldBe Set( +// "$SubRecipe$SubRecipe$InteractionSeven", +// "$SubRecipe$SubSubRecipe$InteractionOne" +// ) + + val vis = RecipeVisualizer.visualizeRecipe(compiledRecipe, RecipeVisualStyle.default) + + println(vis) + } + } + } +} diff --git a/core/baker-compiler/pom.xml b/core/baker-compiler/pom.xml index 073c920c0..5767db0c2 100644 --- a/core/baker-compiler/pom.xml +++ b/core/baker-compiler/pom.xml @@ -16,6 +16,11 @@ Compiler for Baker recipes + + com.ing.baker + baker-compiler-scala + ${project.version} + com.ing.baker baker-compiler-kotlin diff --git a/pom.xml b/pom.xml index b1374856a..c84b18bac 100644 --- a/pom.xml +++ b/pom.xml @@ -113,6 +113,7 @@ core/baker-recipe-dsl core/baker-recipe-dsl-kotlin core/baker-compiler + core/baker-compiler-scala core/baker-compiler-kotlin core/baker-akka-actors core/baker-akka-runtime From 5347e0f185838e3031de0b0675a77d143f08109c Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Wed, 27 May 2026 15:40:47 +0200 Subject: [PATCH 14/16] scala and kotlin compiled recipe comparison --- core/baker-akka-runtime/pom.xml | 15 ++ .../ing/baker/runtime/akka/AkkaBaker.scala | 5 +- .../baker/runtime/akka/BakerSetupSpec.scala | 6 +- core/baker-compiler-kotlin/pom.xml | 4 +- .../baker/compiler/PreCompileValidations.kt | 6 - .../baker/compiler/RecipeCompilerKotlin.kt | 5 +- .../baker/compiler/RecipeCompilerScala.scala | 2 +- core/baker-compiler/pom.xml | 9 +- .../com/ing/baker/compiler/RecipeCompiler.kt | 189 +++++++++++++++++- .../baker/runtime/model/RecipeManager.scala | 8 +- .../model/BakerModelSpecSetupTests.scala | 6 +- .../com/ing/baker/il/CompiledRecipe.scala | 15 ++ .../com/ing/baker/il/RecipeValidations.scala | 4 +- core/baker-test/pom.xml | 7 + http/baker-http-server/pom.xml | 13 ++ 15 files changed, 269 insertions(+), 25 deletions(-) diff --git a/core/baker-akka-runtime/pom.xml b/core/baker-akka-runtime/pom.xml index 79e59defb..4ceeca6ab 100644 --- a/core/baker-akka-runtime/pom.xml +++ b/core/baker-akka-runtime/pom.xml @@ -122,6 +122,21 @@ scalatest-maven-plugin + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + create-test-jar + package + + test-jar + + + + diff --git a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBaker.scala b/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBaker.scala index 71f67c0b0..db1b9a7f9 100644 --- a/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBaker.scala +++ b/core/baker-akka-runtime/src/main/scala/com/ing/baker/runtime/akka/AkkaBaker.scala @@ -111,9 +111,12 @@ class AkkaBaker private[runtime](config: AkkaBakerConfig) extends scaladsl.Baker getImplementationErrors(recipe).flatMap { implementationErrors => if (implementationErrors.nonEmpty) { Future.failed(ImplementationsException(s"Recipe ${recipe.name}:${recipe.recipeId} has implementation errors: ${implementationErrors.mkString(", ")}")) - } else if (recipe.validationErrors.nonEmpty) { + } else if (recipe.criticalValidationErrors.nonEmpty) { Future.failed(RecipeValidationException(s"Recipe ${recipe.name}:${recipe.recipeId} has validation errors: ${recipe.validationErrors.mkString(", ")}")) } else { + if (recipe.nonCriticalValidationErrors.nonEmpty) { + logger.warn(s"Recipe ${recipe.name}:${recipe.recipeId} has validation warnings: ${recipe.nonCriticalValidationErrors.mkString(", ")}") + } recipeManager.put(recipeRecord.copy(updated = updated)) } } diff --git a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala index bc1c224c9..72787e040 100644 --- a/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala +++ b/core/baker-akka-runtime/src/test/scala/com/ing/baker/runtime/akka/BakerSetupSpec.scala @@ -128,7 +128,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { recoverToExceptionIf[RecipeValidationException] { baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe NonProvidedIngredient:f31b181c8e36a5c3 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction"))) + }.map(_ should have('message("Recipe NonProvidedIngredient:68b775e508fc6877 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction"))) } "a recipe does not provide an implementation for an interaction" in { @@ -141,7 +141,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { recoverToExceptionIf[ImplementationsException] { baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe MissingImplementation:224cacdbf4b4e350 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)"))) + }.map(_ should have('message("Recipe MissingImplementation:dc3970efc8837e64 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)"))) } "a recipe provides an implementation for an interaction and does not comply to the Interaction" in { @@ -154,7 +154,7 @@ class BakerSetupSpec extends BakerRuntimeTestBase { recoverToExceptionIf[ImplementationsException] { baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))) - }.map(_ should have('message("Recipe WrongImplementation:165501eaeefc4b2f has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)"))) + }.map(_ should have('message("Recipe WrongImplementation:8e2745de0bb0bde5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)"))) } } } diff --git a/core/baker-compiler-kotlin/pom.xml b/core/baker-compiler-kotlin/pom.xml index 1e9aa854f..b84d1e12c 100644 --- a/core/baker-compiler-kotlin/pom.xml +++ b/core/baker-compiler-kotlin/pom.xml @@ -12,8 +12,8 @@ baker-compiler-kotlin - Baker Recipe Compiler - Compiler for Baker recipes + Baker Kotlin Recipe Compiler + Kotlin Compiler for Baker recipes diff --git a/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt index e15a3cc8d..281ebf2db 100644 --- a/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt +++ b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/PreCompileValidations.kt @@ -4,15 +4,9 @@ import com.ing.baker.recipe.Event import com.ing.baker.recipe.Ingredient import com.ing.baker.recipe.Interaction import com.ing.baker.recipe.Recipe -import com.ing.baker.recipe.toKotlin -import scala.jdk.javaapi.CollectionConverters -import com.ing.baker.recipe.common.Recipe as ScalaRecipe object PreCompileValidations { - fun preCompileAssertions(recipe: ScalaRecipe): scala.collection.immutable.Seq = - CollectionConverters.asScala(preCompileAssertions(recipe.toKotlin())).toSeq() - fun preCompileAssertions(recipe: Recipe): List { assertValidNames(Recipe::name, listOf(recipe), "Recipe") assertValidNames(Interaction::name, recipe.interactions, "Interaction") diff --git a/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt index 7832aa483..341d7d6b9 100644 --- a/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt +++ b/core/baker-compiler-kotlin/src/main/kotlin/com/ing/baker/compiler/RecipeCompilerKotlin.kt @@ -55,6 +55,7 @@ import com.ing.baker.il.CompiledRecipe.`Scala212CompatibleScala$`.`MODULE$` as S import com.ing.baker.il.failurestrategy.InteractionFailureStrategy as ILInteractionFailureStrategy import com.ing.baker.il.`package$`.`MODULE$` as ILPackage import com.ing.baker.il.petrinet.Place.`EmptyEventIngredientPlace$`.`MODULE$` as EmptyEventIngredientPlace +import com.ing.baker.il.petrinet.Place.`EventOrPreconditionPlace$`.`MODULE$` as EventOrPreconditionPlace import com.ing.baker.il.petrinet.Place.`EventPreconditionPlace$`.`MODULE$` as EventPreconditionPlace import com.ing.baker.il.petrinet.Place.`IngredientPlace$`.`MODULE$` as IngredientPlace import com.ing.baker.il.petrinet.Place.`InteractionEventOutputPlace$`.`MODULE$` as InteractionEventOutputPlace @@ -489,7 +490,7 @@ object RecipeCompilerKotlin { val eventTransition = preconditionTransition(eventName) val notProvidedError = when (eventTransition) { - null -> listOf("Event '$eventName' for '$interactionTransition' is not provided in the recipe") + null -> listOf("Event '$eventName' for '${interactionTransition.label()}' is not provided in the recipe") else -> emptyList() } @@ -555,7 +556,7 @@ object RecipeCompilerKotlin { // only one `Place` for all the OR events val eventPreconditionPlace = createPlace( label = "${interaction.name}-or-$index", - placeType = EventPreconditionPlace + placeType = EventOrPreconditionPlace ) orGroup.flatMap { eventName -> buildEventPreconditionArcs( diff --git a/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala index ae834012f..ec1b93f95 100644 --- a/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala +++ b/core/baker-compiler-scala/src/main/scala/com/ing/baker/compiler/RecipeCompilerScala.scala @@ -79,7 +79,7 @@ object RecipeCompilerScala { val eventTransition = preconditionTransition(eventName) val notProvidedError = eventTransition match { - case None => Seq(s"Event '$eventName' for '$interactionTransition' is not provided in the recipe") + case None => Seq(s"Event '$eventName' for '${interactionTransition.label}' is not provided in the recipe") case Some(_) => Seq.empty } diff --git a/core/baker-compiler/pom.xml b/core/baker-compiler/pom.xml index 5767db0c2..4cd582af0 100644 --- a/core/baker-compiler/pom.xml +++ b/core/baker-compiler/pom.xml @@ -43,8 +43,13 @@ - org.scala-lang - scala-library + org.scala-lang.modules + scala-collection-compat_${scala.compat.version} + + + + org.slf4j + slf4j-api diff --git a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt index 041686e05..6b761face 100644 --- a/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt +++ b/core/baker-compiler/src/main/kotlin/com/ing/baker/compiler/RecipeCompiler.kt @@ -1,11 +1,17 @@ package com.ing.baker.compiler import com.ing.baker.il.CompiledRecipe +import com.ing.baker.il.CompiledRecipe.NON_CRITICAL_VALIDATION_MESSAGE_PREFIX import com.ing.baker.il.ValidationSettings import com.ing.baker.recipe.common.Recipe +import org.slf4j.LoggerFactory +import scala.jdk.javaapi.CollectionConverters + object RecipeCompiler { + private val logger = LoggerFactory.getLogger(RecipeCompiler::class.java) + /** * Compile the given recipe to a technical recipe that is useful for Baker. * @@ -17,8 +23,187 @@ object RecipeCompiler { fun compileRecipe( recipe: Recipe, validationSettings: ValidationSettings - ): CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe, validationSettings) + ): CompiledRecipe { + // By default, Scala compiler has a precedence. + // We want to migrate to Kotlin implementation and here we compare the results of both compilers to ensure they produce the same output. + // In case of output difference, we add an error so that we can investigate and fix it before we can remove the Scala implementation. + // Difference in recipe ID is expected and ignored, as it is generated differently in both implementations. + val scala = RecipeCompilerScala.compileRecipe(recipe, validationSettings) + val kotlin = RecipeCompilerKotlin.compileRecipe(recipe, validationSettings) + + val comparison = compareCompiledRecipes(scala, kotlin) + + if (comparison.isEquivalent) + return scala + + // Add comparison failure to validation errors + val updatedErrors = CollectionConverters.asJava(scala.validationErrors()) + .toMutableList() + .apply { add("${NON_CRITICAL_VALIDATION_MESSAGE_PREFIX()} [COMPILATION FAILURE]: $comparison") } + + return CompiledRecipe( + scala.name(), + scala.recipeId(), + scala.petriNet(), + scala.initialMarking(), + CollectionConverters.asScala(updatedErrors).toSeq(), + scala.eventReceivePeriod(), + scala.retentionPeriod() + ) + } @JvmStatic - fun compileRecipe(recipe: Recipe): CompiledRecipe = RecipeCompilerKotlin.compileRecipe(recipe) + fun compileRecipe(recipe: Recipe): CompiledRecipe = + compileRecipe(recipe, ValidationSettings.defaultValidationSettings()) + + /** + * Compare two CompiledRecipe instances, ignoring the recipeId field. + * Returns a ComparisonResult with details about any differences found. + */ + private fun compareCompiledRecipes(scala: CompiledRecipe, kotlin: CompiledRecipe): ComparisonResult { + val differences = mutableListOf() + + // Compare name + if (scala.name() != kotlin.name()) { + differences.add("name: '${scala.name()}' vs '${kotlin.name()}'") + } + + // Compare validation errors + val scalaErrors = CollectionConverters.asJava(scala.validationErrors()).toSet() + val kotlinErrors = CollectionConverters.asJava(kotlin.validationErrors()).toSet() + if (scalaErrors != kotlinErrors) { + differences.add("validationErrors: [${scalaErrors.joinToString(". ")}] vs [${kotlinErrors.joinToString(". ")}]") + } + + // Compare event receive period + if (scala.eventReceivePeriod() != kotlin.eventReceivePeriod()) { + differences.add("eventReceivePeriod: ${scala.eventReceivePeriod()} vs ${kotlin.eventReceivePeriod()}") + } + + // Compare retention period + if (scala.retentionPeriod() != kotlin.retentionPeriod()) { + differences.add("retentionPeriod: ${scala.retentionPeriod()} vs ${kotlin.retentionPeriod()}") + } + + // Compare petri net structure + val petriNetComparison = comparePetriNets(scala, kotlin) + differences.addAll(petriNetComparison) + + // Compare initial marking + val markingComparison = compareInitialMarking(scala, kotlin) + differences.addAll(markingComparison) + + return ComparisonResult(differences.isEmpty(), differences) + .also { comparison -> + if (!comparison.isEquivalent) { + logger.error("Scala and Kotlin compilers produced different results: ${comparison.differences}") + } else { + logger.debug("Scala and Kotlin compilers produced equivalent results") + } + } + } + + private fun comparePetriNets(scala: CompiledRecipe, kotlin: CompiledRecipe): List { + val differences = mutableListOf() + val scalaNet = scala.petriNet() + val kotlinNet = kotlin.petriNet() + + // Compare places - convert Scala collections to Java/Kotlin + val scalaPlaces = CollectionConverters.asJava(scalaNet.places()).toSet() + val kotlinPlaces = CollectionConverters.asJava(kotlinNet.places()).toSet() + if (scalaPlaces != kotlinPlaces) { + val scalaOnly = scalaPlaces - kotlinPlaces + val kotlinOnly = kotlinPlaces - scalaPlaces + if (scalaOnly.isNotEmpty()) { + differences.add("places only in Scala: ${scalaOnly.joinToString { it.label() }}") + } + if (kotlinOnly.isNotEmpty()) { + differences.add("places only in Kotlin: ${kotlinOnly.joinToString { it.label() }}") + } + } + + // Compare transitions + val scalaTransitions = CollectionConverters.asJava(scalaNet.transitions()).toSet() + val kotlinTransitions = CollectionConverters.asJava(kotlinNet.transitions()).toSet() + if (scalaTransitions != kotlinTransitions) { + val scalaOnly = scalaTransitions - kotlinTransitions + val kotlinOnly = kotlinTransitions - scalaTransitions + if (scalaOnly.isNotEmpty()) { + differences.add("transitions only in Scala: ${scalaOnly.joinToString { it.label() }}") + } + if (kotlinOnly.isNotEmpty()) { + differences.add("transitions only in Kotlin: ${kotlinOnly.joinToString { it.label() }}") + } + } + + // Compare sensory events - use Java accessor + val scalaSensoryEvents = scala.sensoryEvents() + val kotlinSensoryEvents = kotlin.sensoryEvents() + if (scalaSensoryEvents != kotlinSensoryEvents) { + val scalaSet = CollectionConverters.asJava(scalaSensoryEvents) + val kotlinSet = CollectionConverters.asJava(kotlinSensoryEvents) + differences.add("sensoryEvents differ: ${scalaSet.size} vs ${kotlinSet.size}") + } + + // Compare all events - use Java accessor getAllEvents + val scalaAllEvents = scala.getAllEvents() + val kotlinAllEvents = kotlin.getAllEvents() + if (scalaAllEvents != kotlinAllEvents) { + val scalaSize = scalaAllEvents.size + val kotlinSize = kotlinAllEvents.size + differences.add("allEvents differ: $scalaSize vs $kotlinSize") + } + + // Compare all ingredients - use Java accessor getAllIngredients + val scalaAllIngredients = scala.getAllIngredients() + val kotlinAllIngredients = kotlin.getAllIngredients() + if (scalaAllIngredients != kotlinAllIngredients) { + val scalaSize = scalaAllIngredients.size + val kotlinSize = kotlinAllIngredients.size + differences.add("allIngredients differ: $scalaSize vs $kotlinSize") + } + + // Compare interaction transitions + val scalaInteractionTransitions = CollectionConverters.asJava(scala.interactionTransitions()).toSet() + val kotlinInteractionTransitions = CollectionConverters.asJava(kotlin.interactionTransitions()).toSet() + if (scalaInteractionTransitions != kotlinInteractionTransitions) { + differences.add("interactionTransitions differ: ${scalaInteractionTransitions.size} vs ${kotlinInteractionTransitions.size}") + } + + return differences + } + + private fun compareInitialMarking(scala: CompiledRecipe, kotlin: CompiledRecipe): List { + val differences = mutableListOf() + val scalaMarking = scala.initialMarking() + val kotlinMarking = kotlin.initialMarking() + + if (scalaMarking != kotlinMarking) { + differences.add("initialMarking differs") + // More detailed comparison if needed + val scalaMarkingJava = CollectionConverters.asJava(scalaMarking) + val kotlinMarkingJava = CollectionConverters.asJava(kotlinMarking) + if (scalaMarkingJava.size != kotlinMarkingJava.size) { + differences.add("initialMarking size: ${scalaMarkingJava.size} vs ${kotlinMarkingJava.size}") + } + } + + return differences + } + + /** + * Result of comparing two CompiledRecipe instances. + */ + data class ComparisonResult( + val isEquivalent: Boolean, + val differences: List + ) { + override fun toString(): String { + return if (isEquivalent) { + "CompiledRecipes are equivalent (ignoring recipeId)" + } else { + "CompiledRecipes differ:\n${differences.joinToString("\n - ", " - ")}" + } + } + } } diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala index 5850d70cd..2fa7012a7 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala +++ b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala @@ -33,9 +33,12 @@ trait RecipeManager[F[_]] extends LazyLogging { _ <- if (implementationErrors.nonEmpty) Sync[F].raiseError(ImplementationsException(s"Recipe ${compiledRecipe.name}:${compiledRecipe.recipeId} has implementation errors: ${implementationErrors.mkString(", ")}")) - else if (compiledRecipe.validationErrors.nonEmpty) + else if (compiledRecipe.criticalValidationErrors.nonEmpty) Sync[F].raiseError(RecipeValidationException(s"Recipe ${compiledRecipe.name}:${compiledRecipe.recipeId} has validation errors: ${compiledRecipe.validationErrors.mkString(", ")}")) - else + else { + if (compiledRecipe.nonCriticalValidationErrors.nonEmpty) { + logger.warn(s"Recipe ${compiledRecipe.name}:${compiledRecipe.recipeId} has validation warnings: ${compiledRecipe.nonCriticalValidationErrors.mkString(", ")}") + } for { timestamp <- async.pure(System.currentTimeMillis()) _ <- store(compiledRecipe, timestamp) @@ -43,6 +46,7 @@ trait RecipeManager[F[_]] extends LazyLogging { _ <- sync.delay(components.logging.addedRecipe(recipeAdded)) _ <- components.eventStream.publish(recipeAdded) } yield () + } } yield compiledRecipe.recipeId def getRecipe(recipeId: String)(implicit components: BakerComponents[F], sync: Sync[F]): F[RecipeInformation] = diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala index b4690e417..44a217f82 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala +++ b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala @@ -96,7 +96,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(mockImplementations) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe NonProvidedIngredient:f31b181c8e36a5c3 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction")) + case Left(e) => e should have('message("Recipe NonProvidedIngredient:68b775e508fc6877 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction")) case Right(_) => fail("Adding a recipe should fail") } } yield succeed @@ -111,7 +111,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List.empty) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe MissingImplementation:224cacdbf4b4e350 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) + case Left(e) => e should have('message("Recipe MissingImplementation:dc3970efc8837e64 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) case Right(_) => fail("Adding a recipe should fail") } } yield succeed @@ -126,7 +126,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(new InteractionOneWrongApply()))) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe WrongImplementation:165501eaeefc4b2f has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) + case Left(e) => e should have('message("Recipe WrongImplementation:8e2745de0bb0bde5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) case Right(_) => fail("Adding an interaction should fail") } } yield succeed diff --git a/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/CompiledRecipe.scala b/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/CompiledRecipe.scala index f3f38469b..b83b8290d 100644 --- a/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/CompiledRecipe.scala +++ b/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/CompiledRecipe.scala @@ -1,5 +1,6 @@ package com.ing.baker.il +import com.ing.baker.il.CompiledRecipe.NON_CRITICAL_VALIDATION_MESSAGE_PREFIX import com.ing.baker.il.petrinet.{EventTransition, InteractionTransition, Place, RecipePetriNet} import com.ing.baker.petrinet.api.Marking @@ -9,6 +10,8 @@ import scala.concurrent.duration.FiniteDuration object CompiledRecipe { + final val NON_CRITICAL_VALIDATION_MESSAGE_PREFIX = "Non critical validation error" + sealed trait RecipeIdVariant sealed trait OldRecipeIdVariant extends RecipeIdVariant case object Scala212CompatibleJava extends OldRecipeIdVariant @@ -166,4 +169,16 @@ case class CompiledRecipe(name: String, @nowarn def getAllIngredients: java.util.Set[IngredientDescriptor] = allIngredients.asJava + + val criticalValidationErrors: Seq[String] = + validationErrors.filterNot(_.startsWith(NON_CRITICAL_VALIDATION_MESSAGE_PREFIX)) + + val nonCriticalValidationErrors: Seq[String] = + validationErrors.filter(_.startsWith(NON_CRITICAL_VALIDATION_MESSAGE_PREFIX)) + + @nowarn + def getCriticalValidationErrors: java.util.List[String] = criticalValidationErrors.toList.asJava + + @nowarn + def getNonCriticalValidationErrors: java.util.List[String] = nonCriticalValidationErrors.toList.asJava } diff --git a/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala b/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala index e96239e60..1b68ad227 100644 --- a/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala +++ b/core/baker-intermediate-language/src/main/scala/com/ing/baker/il/RecipeValidations.scala @@ -4,11 +4,13 @@ import com.ing.baker.il.petrinet.InteractionTransition import com.ing.baker.petrinet.api._ import com.ing.baker.types +import scala.reflect.runtime.universe.This + object RecipeValidations { private def validateInteraction(compiledRecipe: CompiledRecipe)(interactionTransition: InteractionTransition): Seq[String] = { val interactionWithNoRequirementsValidation : Seq[String] = - Some(s"Interaction $interactionTransition does not have any requirements (ingredients or preconditions)! This will result in an infinite execution loop.") + Some(s"Interaction ${interactionTransition.label} does not have any requirements (ingredients or preconditions)! This will result in an infinite execution loop.") .filter(_ => compiledRecipe.petriNet.inMarking(interactionTransition).isEmpty).toIndexedSeq // check if the process id argument type is correct diff --git a/core/baker-test/pom.xml b/core/baker-test/pom.xml index ef4a79c1d..639456734 100644 --- a/core/baker-test/pom.xml +++ b/core/baker-test/pom.xml @@ -59,6 +59,13 @@ test-jar test + + com.ing.baker + baker-compiler-kotlin + ${project.version} + test-jar + test + org.scalatest diff --git a/http/baker-http-server/pom.xml b/http/baker-http-server/pom.xml index 9d9bb0b1c..43d8c0ccd 100644 --- a/http/baker-http-server/pom.xml +++ b/http/baker-http-server/pom.xml @@ -76,6 +76,12 @@ ${project.version} test + + com.ing.baker + baker-compiler + ${project.version} + test + com.ing.baker baker-compiler-scala @@ -83,6 +89,13 @@ test-jar test + + com.ing.baker + baker-compiler-kotlin + ${project.version} + test-jar + test + org.mockito From 7fe515a68cce39f0e9fab7244e17d5dc53631ab3 Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Tue, 9 Jun 2026 09:56:50 +0200 Subject: [PATCH 15/16] renaming interface to runtime; moving in-memory from Scala to Kotlin --- bakery/bakery-interaction-protocol/pom.xml | 2 +- core/baker-akka-runtime/pom.xml | 2 +- .../com/ing/baker/runtime/kotlindsl/Config.kt | 38 -- .../runtime/kotlindsl/RecipeInstanceConfig.kt | 23 - .../TimerInteraction.scala | 17 - .../TimerInteractionJava.scala | 22 - .../runtime/inmemory/InMemoryBaker.scala | 73 --- .../inmemory/InMemoryEventStream.scala | 21 - .../inmemory/InMemoryInteractionManager.scala | 18 - .../InMemoryRecipeInstanceManager.scala | 81 --- .../inmemory/InMemoryRecipeManager.scala | 28 - .../com/ing/baker/runtime/javadsl/Baker.scala | 485 ------------------ .../javadsl/RecipeInstanceConfig.scala | 24 - core/baker-runtime-in-memory/pom.xml | 240 +++++++++ .../baker/runtime/inmemory/InMemoryBaker.kt | 115 +++++ .../runtime/inmemory/InMemoryEventStream.kt | 23 + .../inmemory/InMemoryInteractionManager.kt | 13 + .../inmemory/InMemoryRecipeInstanceManager.kt | 161 ++++++ .../runtime/inmemory/InMemoryRecipeManager.kt | 34 ++ .../runtime/inmemory/InMemoryBakerSpec.scala | 16 +- .../inmemory/InMemoryMemoryCleanupSpec.scala | 105 ++-- .../pom.xml | 13 +- .../com/ing/baker/runtime/kotlindsl/Baker.kt | 0 .../kotlindsl/ClassInteractionInstance.kt | 0 .../runtime/kotlindsl/EventResolutions.kt | 0 .../kotlindsl/FunctionInteractionInstance.kt | 0 .../baker/runtime/kotlindsl/InMemoryBaker.kt | 5 +- .../src/test/kotlin/BakerInterfaceTest.kt | 0 .../kotlin/FunctionInteractionInstanceTest.kt | 0 .../pom.xml | 21 +- .../src/main/protobuf/common.proto | 0 .../com/ing/baker/runtime/common/Baker.scala | 42 +- .../ing/baker/runtime/common/BakerEvent.scala | 0 .../baker/runtime/common/BakerException.scala | 0 .../baker/runtime/common/EventInstance.scala | 0 .../baker/runtime/common/EventMoment.scala | 0 .../runtime/common/EventResolutions.scala | 0 .../runtime/common/IngredientInstance.scala | 0 .../InteractionExecutionFailureReason.java | 0 .../common/InteractionExecutionResult.scala | 0 .../runtime/common/InteractionInstance.scala | 0 .../InteractionInstanceDescriptor.scala | 0 .../common/InteractionInstanceInput.scala | 0 .../common/LanguageDataStructures.scala | 0 .../runtime/common/RecipeEventMetadata.scala | 0 .../runtime/common/RecipeInformation.scala | 0 .../common/RecipeInstanceMetadata.scala | 0 .../runtime/common/RecipeInstanceState.scala | 0 .../baker/runtime/common/RejectReason.java | 0 .../runtime/common/SensoryEventResult.scala | 0 .../runtime/common/SensoryEventStatus.java | 0 .../com/ing/baker/runtime/common/Utils.scala | 0 .../ing/baker/runtime/common/package.scala | 0 .../TimerInteraction.scala | 18 + .../TimerInteractionJava.scala | 23 + .../runtime/defaultinteractions/package.scala | 4 +- .../com/ing/baker/runtime/javadsl/Baker.scala | 379 ++++++++++++++ .../baker/runtime/javadsl/BakerCommon.scala | 392 ++++++++++++++ .../baker/runtime/javadsl/BakerEvent.scala | 0 .../baker/runtime/javadsl/EventInstance.scala | 0 .../baker/runtime/javadsl/EventListener.scala | 0 .../baker/runtime/javadsl/EventMoment.scala | 0 .../runtime/javadsl/EventResolutions.scala | 0 .../runtime/javadsl/IngredientInstance.scala | 0 .../javadsl/InteractionExecutionResult.scala | 0 .../runtime/javadsl/InteractionInstance.scala | 0 .../InteractionInstanceDescriptor.scala | 0 .../javadsl/InteractionInstanceInput.scala | 0 .../runtime/javadsl/RecipeEventMetadata.scala | 0 .../runtime/javadsl/RecipeInformation.scala | 0 .../javadsl/RecipeInstanceMetadata.scala | 0 .../runtime/javadsl/RecipeInstanceState.scala | 0 .../runtime/javadsl/SensoryEventResult.scala | 0 .../baker/runtime/model/BakerComponents.scala | 0 .../baker/runtime/model}/BakerConfig.scala | 44 +- .../com/ing/baker/runtime/model/BakerF.scala | 67 +-- .../baker/runtime/model/BakerLogging.scala | 0 .../ing/baker/runtime/model/EventStream.scala | 0 .../model/FireSensoryEventRejection.scala | 0 .../runtime/model/InteractionInstance.scala | 0 .../runtime/model/InteractionManager.scala | 84 +-- .../runtime/model/RecipeInstanceManager.scala | 17 +- .../baker/runtime/model/RecipeManager.scala | 2 +- .../model/recipeinstance/RecipeInstance.scala | 28 +- .../recipeinstance/RecipeInstanceConfig.scala | 15 + .../RecipeInstanceEventValidation.scala | 0 .../recipeinstance/RecipeInstanceState.scala | 0 .../recipeinstance/TransitionExecution.scala | 0 .../ing/baker/runtime/scaladsl/Baker.scala | 2 + .../baker/runtime/scaladsl/BakerEvent.scala | 0 .../baker/runtime/scaladsl/BakerResult.scala | 0 .../runtime/scaladsl/EventInstance.scala | 0 .../baker/runtime/scaladsl/EventMoment.scala | 0 .../runtime/scaladsl/EventResolutions.scala | 0 .../runtime/scaladsl/EventResolutionsF.scala | 0 .../runtime/scaladsl/IngredientInstance.scala | 0 .../scaladsl/InteractionExecutionResult.scala | 0 .../scaladsl/InteractionInstance.scala | 0 .../InteractionInstanceDescriptor.scala | 0 .../scaladsl/InteractionInstanceInput.scala | 0 .../scaladsl/RecipeEventMetadata.scala | 0 .../runtime/scaladsl/RecipeInformation.scala | 0 .../scaladsl/RecipeInstanceMetadata.scala | 0 .../scaladsl/RecipeInstanceState.scala | 0 .../runtime/scaladsl/SensoryEventResult.scala | 0 .../serialization/AddMetaDataRequest.scala | 0 .../runtime/serialization/BakeRequest.scala | 0 .../runtime/serialization/Encryption.scala | 0 .../serialization/InteractionExecution.scala | 0 .../runtime/serialization/JsonCodec.scala | 0 .../runtime/serialization/JsonDecoders.scala | 0 .../runtime/serialization/JsonEncoders.scala | 0 .../runtime/serialization/ProtoMap.scala | 0 .../serialization/TokenIdentifier.scala | 0 .../protomappings/BakerEventMapping.scala | 0 .../protomappings/BakerExceptionMapping.scala | 0 .../protomappings/BakerTypesMapping.scala | 0 .../protomappings/BakerValuesMapping.scala | 0 .../protomappings/CompiledRecipeMapping.scala | 0 .../EventDescriptorMapping.scala | 0 .../protomappings/EventMomentMapping.scala | 0 .../EventOutputTransformerMapping.scala | 0 .../IngredientDescriptorMapping.scala | 0 .../IngredientInstanceMapping.scala | 0 .../InteractionFailureStrategyMapping.scala | 0 .../protomappings/ProcessStateMapping.scala | 0 .../RecipeEventMetadataMapping.scala | 0 .../RecipeInformationMapping.scala | 0 .../RecipeInstanceMetadataMapping.scala | 0 .../protomappings/RuntimeEventMapping.scala | 0 .../SensoryEventResultMapping.scala | 0 .../SensoryEventStatusMappingHelper.scala | 0 .../runtime/javadsl/AnnotatedInteraction.java | 0 .../javadsl/AnnotatedInteraction2.java | 0 .../javadsl/AnnotatedInteraction3.java | 0 .../javadsl/AnnotatedParentInteraction.java | 0 .../runtime/javadsl/MultipleApplyMethods.java | 0 .../runtime/javadsl/NamedInteraction.java | 0 .../runtime/javadsl/NamedInteraction2.java | 0 .../runtime/javadsl/NamedInteraction3.java | 0 .../baker/runtime/javadsl/NoApplyMethod.java | 0 .../javadsl/ParentInteractionWithName.java | 0 .../javadsl/ParentInteractionWithoutName.java | 0 .../baker/runtime/javadsl/SimpleEvent.java | 0 .../runtime/javadsl/SimpleInteraction.java | 0 .../src/test/resources/logback-test.xml | 0 .../javadsl/InteractionInstanceTest.scala | 0 .../runtime/model/BakerModelFixtures.scala | 0 .../baker/runtime/model/BakerModelSpec.scala | 0 .../model/BakerModelSpecEdgeCasesTests.scala | 0 .../model/BakerModelSpecEnquireTests.scala | 0 ...akerModelSpecExecutionSemanticsTests.scala | 0 .../model/BakerModelSpecFailureTests.scala | 0 .../model/BakerModelSpecSetupTests.scala | 12 +- .../runtime/model/RuntimeEventMatcher.scala | 0 .../ing/baker/runtime/model/TestRecipe.scala | 0 .../scaladsl/InteractionInstanceTest.scala | 0 .../runtime/scaladsl/ScalaDSLRuntime.scala | 0 .../serialization/JsonDecodersSpec.scala | 0 .../serialization/JsonEncodersSpec.scala | 0 .../com/ing/bakery/utils/BakeryFunSpec.scala | 0 core/baker-test/pom.xml | 2 +- examples/baker-example/pom.xml | 5 + examples/docs-code-snippets/pom.xml | 2 +- .../scala/application/WebShopApp.scala | 9 +- http/baker-http-client/pom.xml | 2 +- http/baker-http-server/pom.xml | 2 +- pom.xml | 9 +- 168 files changed, 1666 insertions(+), 1074 deletions(-) delete mode 100644 core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Config.kt delete mode 100644 core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/RecipeInstanceConfig.kt delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryBaker.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryEventStream.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala delete mode 100644 core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceConfig.scala create mode 100644 core/baker-runtime-in-memory/pom.xml create mode 100644 core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt create mode 100644 core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt create mode 100644 core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt create mode 100644 core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt create mode 100644 core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt rename core/{baker-interface => baker-runtime-in-memory}/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala (55%) rename core/{baker-interface => baker-runtime-in-memory}/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala (70%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/pom.xml (90%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt (100%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt (100%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt (100%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt (100%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt (75%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/src/test/kotlin/BakerInterfaceTest.kt (100%) rename core/{baker-interface-kotlin => baker-runtime-kotlin}/src/test/kotlin/FunctionInteractionInstanceTest.kt (100%) rename core/{baker-interface => baker-runtime}/pom.xml (90%) rename core/{baker-interface => baker-runtime}/src/main/protobuf/common.proto (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/Baker.scala (91%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/BakerException.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/RejectReason.java (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/Utils.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/common/package.scala (100%) create mode 100644 core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala create mode 100644 core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala (71%) create mode 100644 core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala create mode 100644 core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala (100%) rename core/{baker-interface/src/main/scala/com/ing/baker/runtime/javadsl => baker-runtime/src/main/scala/com/ing/baker/runtime/model}/BakerConfig.scala (58%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/BakerF.scala (93%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/EventStream.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala (95%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala (95%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala (98%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala (93%) create mode 100644 core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala (99%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala (100%) rename core/{baker-interface => baker-runtime}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java (100%) rename core/{baker-interface => baker-runtime}/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java (100%) rename core/{baker-interface => baker-runtime}/src/test/resources/logback-test.xml (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala (86%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala (100%) rename core/{baker-interface => baker-runtime}/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala (100%) diff --git a/bakery/bakery-interaction-protocol/pom.xml b/bakery/bakery-interaction-protocol/pom.xml index 6c6e120dd..26eab174f 100644 --- a/bakery/bakery-interaction-protocol/pom.xml +++ b/bakery/bakery-interaction-protocol/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-interface + baker-runtime ${project.version} diff --git a/core/baker-akka-runtime/pom.xml b/core/baker-akka-runtime/pom.xml index 4ceeca6ab..5eb08e625 100644 --- a/core/baker-akka-runtime/pom.xml +++ b/core/baker-akka-runtime/pom.xml @@ -33,7 +33,7 @@ com.ing.baker - baker-interface + baker-runtime ${project.version} diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Config.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Config.kt deleted file mode 100644 index 0fd9a202a..000000000 --- a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Config.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.ing.baker.runtime.kotlindsl - -import com.ing.baker.runtime.model.BakerF -import scala.concurrent.duration.FiniteDuration -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -data class Config( - val allowAddingRecipeWithoutRequiringInstances: Boolean = false, - val recipeInstanceConfig: RecipeInstanceConfig = RecipeInstanceConfig(), - val idleTimeout: Duration = 60.seconds, - val retentionPeriodCheckInterval: Duration = 10.seconds, - val bakeTimeout: Duration = 10.seconds, - val processEventTimeout: Duration = 10.seconds, - val inquireTimeout: Duration = 60.seconds, - val shutdownTimeout: Duration = 10.seconds, - val addRecipeTimeout: Duration = 10.seconds, - val executeSingleInteractionTimeout: Duration = 60.seconds) { - - private fun Duration.toFiniteDuration(): FiniteDuration { - return FiniteDuration.fromNanos(this.inWholeNanoseconds) - } - - fun toBakerFConfig(): BakerF.Config { - return BakerF.Config( - allowAddingRecipeWithoutRequiringInstances, - recipeInstanceConfig.toBakerFRecipeInstanceConfig(), - idleTimeout.toFiniteDuration(), - retentionPeriodCheckInterval.toFiniteDuration(), - bakeTimeout.toFiniteDuration(), - processEventTimeout.toFiniteDuration(), - inquireTimeout.toFiniteDuration(), - shutdownTimeout.toFiniteDuration(), - addRecipeTimeout.toFiniteDuration(), - executeSingleInteractionTimeout.toFiniteDuration() - ) - } -} \ No newline at end of file diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/RecipeInstanceConfig.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/RecipeInstanceConfig.kt deleted file mode 100644 index 914649ba8..000000000 --- a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/RecipeInstanceConfig.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.ing.baker.runtime.kotlindsl - -import com.ing.baker.runtime.model.recipeinstance.RecipeInstance -import scala.Option -import scala.concurrent.duration.FiniteDuration -import scala.jdk.CollectionConverters -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -data class RecipeInstanceConfig( - val idleTTL: Duration? = 5.seconds, - val ingredientsFilter: List = emptyList()) { - - private fun Duration.toFiniteDuration(): FiniteDuration { - return FiniteDuration.fromNanos(this.inWholeNanoseconds) - } - fun toBakerFRecipeInstanceConfig(): RecipeInstance.Config { - return RecipeInstance.Config( - idleTTL?.let{ Option.apply(it.toFiniteDuration()) } ?: Option.empty(), - CollectionConverters.ListHasAsScala(ingredientsFilter).asScala().toSeq() - ) - } -} \ No newline at end of file diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala deleted file mode 100644 index 87feaceed..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala +++ /dev/null @@ -1,17 +0,0 @@ -package com.ing.baker.runtime.defaultinteractions - -import cats.effect.IO - -import scala.concurrent.duration.FiniteDuration - -class TimerInteraction(skipWait: Boolean) { - - class TimeWaited() - - def apply(WaitTime: FiniteDuration): IO[TimeWaited] = { - if(skipWait) - IO.pure(new TimeWaited) - else - IO.sleep(WaitTime) *> IO(new TimeWaited) - } -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala deleted file mode 100644 index 2710c48b5..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala +++ /dev/null @@ -1,22 +0,0 @@ -package com.ing.baker.runtime.defaultinteractions - -import java.time.Duration -import java.util.concurrent.TimeUnit - -import cats.effect.IO - -import scala.concurrent.duration.FiniteDuration - -class TimerInteractionJava(skipWait: Boolean) { - - class TimeWaited() - - val name = "TimerInteraction" - - def apply(WaitTime: Duration): IO[TimeWaited] = { - if(skipWait) - IO.pure(new TimeWaited) - else - IO.sleep(FiniteDuration.apply(WaitTime.toMillis, TimeUnit.MILLISECONDS)) *> IO(new TimeWaited) - } -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryBaker.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryBaker.scala deleted file mode 100644 index 2ff9c9d52..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryBaker.scala +++ /dev/null @@ -1,73 +0,0 @@ -package com.ing.baker.runtime.inmemory - -import cats.effect.IO -import cats.effect.unsafe.IORuntime -import cats.~> -import com.ing.baker.runtime.javadsl.BakerConfig -import com.ing.baker.runtime.model.{BakerComponents, BakerF, InteractionInstance} -import com.ing.baker.runtime.{defaultinteractions, javadsl} - -import java.util -import java.util.{List => JavaList} -import scala.annotation.nowarn -import scala.jdk.CollectionConverters._ -import scala.concurrent._ - -object InMemoryBaker { - - def build(config: BakerF.Config = BakerF.Config(), - implementations: List[InteractionInstance[IO]]): IO[BakerF[IO]] = for { - recipeInstanceManager <- InMemoryRecipeInstanceManager.build(config.idleTimeout, config.retentionPeriodCheckInterval) - recipeManager <- InMemoryRecipeManager.build - eventStream <- InMemoryEventStream.build - interactions <- InMemoryInteractionManager.build(implementations ++ defaultinteractions.all) - } yield buildWith(BakerComponents[IO](interactions, recipeInstanceManager, recipeManager, eventStream), config) - - def buildWith(components: BakerComponents[IO], - config: BakerF.Config = BakerF.Config()): BakerF[IO] = { - implicit val c: BakerComponents[IO] = components - new InMemoryBakerImpl(config) - } - - @nowarn - def java(config: BakerF.Config, implementations: JavaList[AnyRef]): javadsl.Baker = { - val interactions = implementations.asScala.map{ - case it: InteractionInstance[IO] => it - case it: com.ing.baker.runtime.javadsl.InteractionInstance => - it.asScala.translate[IO](new (Future ~> IO) { - def apply[A](fa: Future[A]): IO[A] = IO.fromFuture(IO(fa)) - }) - case it => InteractionInstance.unsafeFrom[IO](it) - } - val bakerF = build(config, interactions.toList) - .unsafeRunSync()(IORuntime.global).asDeprecatedFutureImplementation( - new (IO ~> Future) { - def apply[A](fa: IO[A]): Future[A] = fa.unsafeToFuture()(IORuntime.global) - }, - new (Future ~> IO) { - def apply[A](fa: Future[A]): IO[A] = IO.fromFuture(IO(fa)) - } - ) - new javadsl.Baker(bakerF) - } - - def java(config: BakerConfig, implementations: JavaList[AnyRef]): javadsl.Baker = { - java(config.toBakerFConfig(), implementations) - } - - def java(implementations: JavaList[AnyRef]): javadsl.Baker = { - java(BakerF.Config(), implementations) - } - - def java(): javadsl.Baker = { - java(BakerF.Config(), new util.ArrayList[AnyRef]()) - } -} - -final class InMemoryBakerImpl(val config: BakerF.Config)(implicit components: BakerComponents[IO]) extends BakerF[IO] { - - /** - * Attempts to gracefully shutdown the baker system. - */ - override def gracefulShutdown(): IO[Unit] = IO.unit -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryEventStream.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryEventStream.scala deleted file mode 100644 index 8fd5eaf20..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryEventStream.scala +++ /dev/null @@ -1,21 +0,0 @@ -package com.ing.baker.runtime.inmemory - -import cats.effect.{IO, Ref} -import com.ing.baker.runtime.model.EventStream -import com.ing.baker.runtime.scaladsl.BakerEvent -import com.typesafe.scalalogging.LazyLogging - -object InMemoryEventStream { - - def build: IO[InMemoryEventStream] = - Ref.of[IO, List[BakerEvent => Unit]](List.empty).map(new InMemoryEventStream(_)) -} - -class InMemoryEventStream(store: Ref[IO, List[BakerEvent => Unit]]) extends EventStream[IO] with LazyLogging { - - override def fetchListeners: IO[List[BakerEvent => Unit]] = - store.get - - override def subscribe(listenerFunction: BakerEvent => Unit): IO[Unit] = - store.update(listenerFunction :: _) -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.scala deleted file mode 100644 index b3d7189d3..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.scala +++ /dev/null @@ -1,18 +0,0 @@ -package com.ing.baker.runtime.inmemory - -import cats.effect.IO -import com.ing.baker.runtime.model.{InteractionInstance, InteractionManager} - -object InMemoryInteractionManager { - - def build(implementations: List[InteractionInstance[IO]]): IO[InMemoryInteractionManager] = - IO(new InMemoryInteractionManager(implementations)) -} - -final class InMemoryInteractionManager(implementations: List[InteractionInstance[IO]]) extends InteractionManager[IO] { - - override val allowSupersetForOutputTypes: Boolean = false - - override def listAll: IO[List[InteractionInstance[IO]]] = IO(implementations) - -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.scala deleted file mode 100644 index e33c02281..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.scala +++ /dev/null @@ -1,81 +0,0 @@ -package com.ing.baker.runtime.inmemory - -import cats.effect.unsafe.implicits.global -import cats.effect.{IO, Ref} -import cats.syntax.all._ -import com.google.common.cache.{CacheBuilder, CacheLoader} -import com.ing.baker.runtime.common.BakerException.NoSuchProcessException -import com.ing.baker.runtime.model.RecipeInstanceManager.RecipeInstanceStatus -import com.ing.baker.runtime.model.RecipeInstanceManager.RecipeInstanceStatus.Active -import com.ing.baker.runtime.model.recipeinstance.RecipeInstance -import com.ing.baker.runtime.model.{BakerComponents, RecipeInstanceManager} -import com.ing.baker.runtime.scaladsl.RecipeInstanceMetadata - -import java.util.concurrent.ConcurrentMap -import scala.annotation.nowarn -import scala.concurrent.duration.FiniteDuration -import scala.jdk.CollectionConverters._ - -object InMemoryRecipeInstanceManager { - - type Store = ConcurrentMap[String, RecipeInstanceStatus[IO]] - - def build(idleTimeOut: FiniteDuration, retentionPeriodCheckInterval: FiniteDuration): IO[InMemoryRecipeInstanceManager] = { - val cache: ConcurrentMap[String, RecipeInstanceStatus[IO]] = CacheBuilder.newBuilder() - .build(new CacheLoader[String, RecipeInstanceStatus[IO]] { - override def load(key: String): RecipeInstanceStatus[IO] = throw NoSuchProcessException("key") - }).asMap() - Ref.of[IO, Store](cache).map(new InMemoryRecipeInstanceManager(_, retentionPeriodCheckInterval, idleTimeOut)) - } -} - -final class InMemoryRecipeInstanceManager(inmem: Ref[IO, InMemoryRecipeInstanceManager.Store], - retentionPeriodCheckInterval: FiniteDuration, - idleTimeOut: FiniteDuration) extends RecipeInstanceManager[IO] { - - // We use this function instead of the startRetentionPeriodStream stream since it performs better - def repeat(io : IO[Unit]) : IO[Nothing] = io >> IO.sleep(retentionPeriodCheckInterval) >> repeat(io) - - repeat(cleanupRecipeInstances(idleTimeOut)).unsafeRunAndForget() - - override def fetch(recipeInstanceId: String): IO[Option[RecipeInstanceStatus[IO]]] = { - inmem.getAndUpdate(store => { - Option.apply(store.get(recipeInstanceId)) match { - case Some(recipeInstance: Active[IO]) => - store.put(recipeInstanceId, recipeInstance.copy(lastModified = System.currentTimeMillis())) - store - case _ => store - } - }).map(store => Option.apply(store.get(recipeInstanceId)) - ) - } - - override def store(newRecipeInstance: RecipeInstance[IO])(implicit components: BakerComponents[IO]): IO[Unit] = - inmem.update(store => { - store.put(newRecipeInstance.recipeInstanceId, RecipeInstanceStatus.Active(newRecipeInstance, System.currentTimeMillis())) - store - }) - - override def idleStop(recipeInstanceId: String): IO[Unit] = - IO.unit - - @nowarn - override def getAllRecipeInstancesMetadata: IO[Set[RecipeInstanceMetadata]] = - inmem.get.flatMap(_.asScala.toMap.toList.traverse { - case (recipeInstanceId, RecipeInstanceStatus.Active(recipeInstance, _)) => - recipeInstance.state.get.map(currentState => RecipeInstanceMetadata(currentState.recipe.recipeId, recipeInstanceId, currentState.createdOn)) - case (recipeInstanceId, RecipeInstanceStatus.Deleted(recipeId, createdOn, _)) => - IO.pure(RecipeInstanceMetadata(recipeId, recipeInstanceId, createdOn)) - }).map(_.toSet) - - @nowarn - override protected def fetchAll: IO[Map[String, RecipeInstanceStatus[IO]]] = - inmem.get.map(_.asScala.toMap) - - override def remove(recipeInstanceId: String): IO[Unit] = - idleStop(recipeInstanceId) *> - inmem.update(store => { - store.remove(recipeInstanceId) - store - }) -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.scala deleted file mode 100644 index 5d2425e76..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.scala +++ /dev/null @@ -1,28 +0,0 @@ -package com.ing.baker.runtime.inmemory - -import cats.effect.{IO, Ref} -import com.ing.baker.il.CompiledRecipe -import com.ing.baker.runtime.common.RecipeRecord -import com.ing.baker.runtime.model.RecipeManager - -object InMemoryRecipeManager { - - type Store = Map[String, RecipeRecord] - - def build: IO[InMemoryRecipeManager] = - Ref.of[IO, Store](Map.empty).map(new InMemoryRecipeManager(_)) -} - -final class InMemoryRecipeManager(inmem: Ref[IO, InMemoryRecipeManager.Store]) extends RecipeManager[IO] { - - import InMemoryRecipeManager._ - - override def store(compiledRecipe: CompiledRecipe, timestamp: Long): IO[Unit] = - inmem.update(_ + (compiledRecipe.recipeId -> RecipeRecord.of(compiledRecipe, updated = timestamp))) - - override def fetch(recipeId: String): IO[Option[RecipeRecord]] = - inmem.get.map(_.get(recipeId)) - - override def fetchAll: IO[Store] = - inmem.get -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala deleted file mode 100644 index 6fefa2ba3..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala +++ /dev/null @@ -1,485 +0,0 @@ -package com.ing.baker.runtime.javadsl - -import com.ing.baker.il.{CompiledRecipe, RecipeVisualStyle} -import com.ing.baker.runtime.common.LanguageDataStructures.JavaApi -import com.ing.baker.runtime.common.{RecipeRecord, SensoryEventStatus} -import com.ing.baker.runtime.{common, scaladsl} -import com.ing.baker.types.Value -import java.time.Duration - -import java.util -import java.util.Optional -import java.util.concurrent.{CompletableFuture, TimeUnit} -import java.util.function.{BiConsumer, Consumer} -import javax.annotation.Nonnull -import scala.annotation.nowarn -import scala.jdk.CollectionConverters._ -import scala.jdk.FutureConverters._ -import scala.concurrent.duration._ -import scala.concurrent.{Await, Future} - -class Baker(private val baker: scaladsl.Baker) extends common.Baker[CompletableFuture] with JavaApi with AutoCloseable { - - /** - * Get underlying baker instance, which provides the scala api. - */ - def getScalaBaker: scaladsl.Baker = baker - - override type SensoryEventResultType = SensoryEventResult - - override type EventResolutionsType = EventResolutions - - override type EventInstanceType = EventInstance - - override type RecipeInstanceStateType = RecipeInstanceState - - override type InteractionInstanceType = InteractionInstance - - override type InteractionInstanceDescriptorType = InteractionInstanceDescriptor - - override type IngredientInstanceType = IngredientInstance - - override type BakerEventType = BakerEvent - - override type RecipeInstanceMetadataType = RecipeInstanceMetadata - - override type RecipeInformationType = RecipeInformation - - override type EventMomentType = EventMoment - - override type RecipeMetadataType = RecipeEventMetadata - - override type InteractionExecutionResultType = InteractionExecutionResult - - override type DurationType = Duration - - override def close(): Unit = { - Await.result(baker.gracefulShutdown(), 10.seconds) - } - - /** - * Adds a recipe to baker and returns a recipeId for the recipe. - * - * This function is idempotent, if the same (equal) recipe was added earlier this will return the same recipeId. - * - * @param recipeRecord The RecipeRecord recipe. - * @return A recipe identifier. - */ - def addRecipe(@Nonnull recipeRecord: RecipeRecord): CompletableFuture[String] = - toCompletableFuture(baker.addRecipe(recipeRecord)) - - - /** - * Adds a recipe to baker and returns a recipeId for the recipe. - * - * This function is idempotent, if the same (equal) recipe was added earlier this will return the same recipeId - * - * @param compiledRecipe The compiled recipe. - * @return A recipeId - */ - override def addRecipe(compiledRecipe: CompiledRecipe, timeCreated: Long, validate: Boolean): CompletableFuture[String] = addRecipe(RecipeRecord.of(compiledRecipe, updated = timeCreated, validate = validate)) - - /** - * Adds a recipe to baker and returns a recipeId for the recipe. - * - * This function is idempotent, if the same (equal) recipe was added earlier this will return the same recipeId - * - * @param compiledRecipe The compiled recipe. - * @return A recipeId - */ - override def addRecipe(compiledRecipe: CompiledRecipe, validate: Boolean): CompletableFuture[String] = addRecipe(compiledRecipe, System.currentTimeMillis(), validate) - - /** - * Attempts to gracefully shutdown the baker system. - */ - def gracefulShutdown(): CompletableFuture[Unit] = - toCompletableFuture(baker.gracefulShutdown()) - - /** - * This bakes (creates) a new process instance of the recipe. - * - * @param recipeId The recipe this instance will be baked for - * @param recipeInstanceId The process identifier - */ - def bake(@Nonnull recipeId: String, @Nonnull recipeInstanceId: String): CompletableFuture[Unit] = - toCompletableFuture(baker.bake(recipeId, recipeInstanceId)) - - /** - * Creates a process instance for the given recipeId with the given RecipeInstanceId as identifier - * This variant also gets a metadata map added on bake. - * This is similar to calling addMetaData after doing the regular bake but depending on the implementation this can be more optimized. - * - * @param recipeId The recipeId for the recipe to bake - * @param recipeInstanceId The identifier for the newly baked process - * @param metadata - * @return - */ - def bake(recipeId: String, recipeInstanceId: String, metadata: util.Map[String, String]): CompletableFuture[Unit] = { - toCompletableFuture(baker.bake(recipeId, recipeInstanceId, metadata.asScala.toMap)) - } - - def deleteRecipeInstance(recipeInstanceId: String, removeFromIndex: Boolean): CompletableFuture[Unit] = { - toCompletableFuture(baker.deleteRecipeInstance(recipeInstanceId, removeFromIndex)) - } - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") - def fireEventAndResolveWhenReceived(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: String): CompletableFuture[SensoryEventStatus] = - fireEventAndResolveWhenReceived(recipeInstanceId, event, Optional.of(correlationId)) - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitCompleted.", "5.1.0") - def fireEventAndResolveWhenCompleted(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: String): CompletableFuture[SensoryEventResult] = - fireEventAndResolveWhenCompleted(recipeInstanceId, event, Optional.of(correlationId)) - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitEvent.", "5.1.0") - def fireEventAndResolveOnEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull onEvent: String, @Nonnull correlationId: String): CompletableFuture[SensoryEventResult] = - fireEventAndResolveOnEvent(recipeInstanceId, event, onEvent, Optional.of(correlationId)) - - @Deprecated - @deprecated("This method uses a callback-style API that is deprecated and will be removed after December 1st, 2026. Please use the new composable API: fireSensoryEventAndAwaitReceived followed by awaitCompleted or awaitEvent.", "5.1.0") - def fireEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: String): EventResolutions = - fireEvent(recipeInstanceId, event, Optional.of(correlationId)) - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") - def fireEventAndResolveWhenReceived(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance): CompletableFuture[SensoryEventStatus] = - fireEventAndResolveWhenReceived(recipeInstanceId, event, Optional.empty[String]()) - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitCompleted.", "5.1.0") - def fireEventAndResolveWhenCompleted(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance): CompletableFuture[SensoryEventResult] = - fireEventAndResolveWhenCompleted(recipeInstanceId, event, Optional.empty[String]()) - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitEvent.", "5.1.0") - def fireEventAndResolveOnEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull onEvent: String): CompletableFuture[SensoryEventResult] = - fireEventAndResolveOnEvent(recipeInstanceId, event, onEvent, Optional.empty[String]()) - - @Deprecated - @deprecated("This method uses a callback-style API that is deprecated and will be removed after December 1st, 2026. Please use the new composable API: fireSensoryEventAndAwaitReceived followed by awaitCompleted or awaitEvent.", "5.1.0") - def fireEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance): EventResolutions = - fireEvent(recipeInstanceId, event, Optional.empty[String]()) - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") - def fireEventAndResolveWhenReceived(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventStatus] = - toCompletableFuture(baker.fireEventAndResolveWhenReceived(recipeInstanceId, event.asScala, Option.apply(correlationId.orElse(null)))) - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitCompleted.", "5.1.0") - @nowarn - def fireEventAndResolveWhenCompleted(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventResult] = - toCompletableFuture(baker.fireEventAndResolveWhenCompleted(recipeInstanceId, event.asScala, Option.apply(correlationId.orElse(null)))).thenApply { result => - SensoryEventResult( - sensoryEventStatus = result.sensoryEventStatus, - eventNames = result.eventNames.asJava, - ingredients = result.ingredients.asJava - ) - } - - @Deprecated - @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitEvent.", "5.1.0") - @nowarn - def fireEventAndResolveOnEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull onEvent: String, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventResult] = - toCompletableFuture(baker.fireEventAndResolveOnEvent(recipeInstanceId, event.asScala, onEvent, Option.apply(correlationId.orElse(null)))).thenApply { result => - SensoryEventResult( - sensoryEventStatus = result.sensoryEventStatus, - eventNames = result.eventNames.asJava, - ingredients = result.ingredients.asJava - ) - } - - @Deprecated - @deprecated("This method uses a callback-style API that is deprecated and will be removed after December 1st, 2026. Please use the new composable API: fireSensoryEventAndAwaitReceived followed by awaitCompleted or awaitEvent.", "5.1.0") - @nowarn - def fireEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): EventResolutions = { - val scalaResult = baker.fireEvent(recipeInstanceId, event.asScala) - EventResolutions( - resolveWhenReceived = toCompletableFuture(scalaResult.resolveWhenReceived), - resolveWhenCompleted = toCompletableFuture(scalaResult.resolveWhenCompleted).thenApply { result => - SensoryEventResult( - sensoryEventStatus = result.sensoryEventStatus, - eventNames = result.eventNames.asJava, - ingredients = result.ingredients.asJava - ) - }) - } - - /** - * Retries a blocked interaction. - * - * @param recipeInstanceId The process identifier. - * @param interactionName The name of the blocked interaction. - * @return - */ - def retryInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String): CompletableFuture[Unit] = - toCompletableFuture(baker.retryInteraction(recipeInstanceId, interactionName)) - - /** - * Resolves a blocked interaction by giving it's output. - * - * @param recipeInstanceId The process identifier. - * @param interactionName The name of the blocked interaction. - * @param event The output of the interaction. - * @return - */ - def resolveInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String, @Nonnull event: EventInstance): CompletableFuture[Unit] = - toCompletableFuture(baker.resolveInteraction(recipeInstanceId, interactionName, event.asScala)) - - /** - * Stops a retrying interaction. - * - * @param recipeInstanceId The process identifier. - * @param interactionName The name of the retrying interaction. - * @return - */ - def stopRetryingInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String): CompletableFuture[Unit] = - toCompletableFuture(baker.stopRetryingInteraction(recipeInstanceId, interactionName)) - - /** - * Check if recipe instance exists and is active. - * - * @param recipeInstanceId The recipe instance identifier - * @return true if recipe instance exists, and is in active state, otherwise false. - */ - def hasRecipeInstance(recipeInstanceId: String): CompletableFuture[Boolean] = - toCompletableFuture(baker.hasRecipeInstance(recipeInstanceId)) - - /** - * Returns the state of a process instance. This includes the ingredients and names of the events. - * - * @param recipeInstanceId The process identifier - * @return The state of the process instance - */ - def getRecipeInstanceState(@Nonnull recipeInstanceId: String): CompletableFuture[RecipeInstanceState] = - toCompletableFuture(baker.getRecipeInstanceState(recipeInstanceId)).thenApply(_.asJava) - - /** - * @param recipeInstanceId The recipeInstance Id. - * @param name The name of the ingredient. - * @return The provided ingredients. - */ - override def getIngredient(recipeInstanceId: String, name: String): CompletableFuture[Value] = - toCompletableFuture(baker.getIngredient(recipeInstanceId, name)) - - /** - * Returns all the ingredients that are accumulated for a given process. - * - * @param recipeInstanceId The process identifier - * @return - */ - def getIngredients(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.Map[String, Value]] = - toCompletableFutureMap(baker.getIngredients(recipeInstanceId)) - - /** - * Returns all fired events for a given RecipeInstance id. - * - * @param recipeInstanceId The process id. - * @return The events - */ - @nowarn - def getEvents(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.List[EventMoment]] = - toCompletableFuture(baker.getEvents(recipeInstanceId)).thenApply(_.map(_.asJava()).asJava) - - /** - * Returns all names of fired events for a given RecipeInstance id. - * - * @param recipeInstanceId The process id. - * @return The event names - */ - @nowarn - def getEventNames(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.List[String]] = - toCompletableFuture(baker.getEventNames(recipeInstanceId)).thenApply(_.asJava) - - /** - * Returns the recipe information for the given RecipeId - * - * @param recipeId the recipeId - * @return The JRecipeInformation recipe - */ - def getRecipe(@Nonnull recipeId: String): CompletableFuture[RecipeInformation] = - toCompletableFuture(baker.getRecipe(recipeId)).thenApply(_.asJava) - - - def getRecipeVisual(recipeId: String, style: RecipeVisualStyle): CompletableFuture[String] = - toCompletableFuture(baker.getRecipeVisual(recipeId)) - - /** - * Return alls 'active' recipes added to this Baker - * - * @return A map with all recipes from recipeId -> JRecipeInformation - */ - @nowarn - def getAllRecipes: CompletableFuture[java.util.Map[String, RecipeInformation]] = - baker.getAllRecipes.asJava.toCompletableFuture.thenApply(_.view.map { case (key, value) => (key, value.asJava)}.toMap.asJava) - - @nowarn - def getInteraction(interactionName: String): CompletableFuture[Optional[InteractionInstanceDescriptorType]] = - baker.getInteraction(interactionName).asJava - .toCompletableFuture - .thenApply(e => Optional.ofNullable(e.map(_.asJava()).orNull)) - - @nowarn - def getAllInteractions: CompletableFuture[java.util.List[InteractionInstanceDescriptorType]] = - baker.getAllInteractions.asJava - .toCompletableFuture - .thenApply(_.map(_.asJava()).asJava) - - @nowarn - def executeSingleInteraction(interactionId: String, ingredients: util.List[IngredientInstanceType]): CompletableFuture[InteractionExecutionResult] = - baker.executeSingleInteraction(interactionId, ingredients.asScala.map(_.asScala).toIndexedSeq).asJava - .toCompletableFuture - .thenApply(_.asJava) - - /** - * Returns an index of all processes. - * - * Can potentially return a partial index when baker runs in cluster mode - * and not all shards can be reached within the given timeout. - * - * Does not include deleted processes. - * - * @return An index of all processes - */ - @nowarn - def getAllRecipeInstancesMetadata: CompletableFuture[util.Set[RecipeInstanceMetadata]] = - baker.getAllRecipeInstancesMetadata.asJava - .toCompletableFuture - .thenApply(_.map(_.asJava).asJava) - - /** - * Registers a listener to all runtime events for this baker instance. - * - * Note that: - * - * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). - * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. - * - * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: - * - * - logging - * - metrics - * - unit tests - * - ... - * - * @param recipeName the name of all recipes this event listener should be triggered for - * @param listenerFunction The listener to subscribe to events. - */ - override def registerEventListener(@Nonnull recipeName: String, @Nonnull listenerFunction: BiConsumer[RecipeEventMetadata, String]): CompletableFuture[Unit] = - toCompletableFuture(baker.registerEventListener(recipeName, - (recipeEventMetadata: scaladsl.RecipeEventMetadata, event: String) => listenerFunction.accept(recipeEventMetadata.asJava, event))) - - /** - * Registers a listener function to all runtime events for this baker instance. - * - * Note that: - * - * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). - * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. - * - * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: - * - * - logging - * - metrics - * - unit tests - * - ... - * - * @param listenerFunction The listener function that is called once these events occur - */ - override def registerEventListener(@Nonnull listenerFunction: BiConsumer[RecipeEventMetadata, String]): CompletableFuture[Unit] = - toCompletableFuture(baker.registerEventListener( - (recipeEventMetadata: scaladsl.RecipeEventMetadata, event: String) => listenerFunction.accept(recipeEventMetadata.asJava, event))) - - /** - * Registers a listener function to all runtime events for this baker instance. - * - * Note that: - * - * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). - * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. - * - * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: - * - * - logging - * - metrics - * - unit tests - * - ... - * - * @param eventListener The EventListener class the processEvent will be called once these events occur - */ - @Deprecated - @deprecated(message = "Replaced with the consumer function variant", since = "3.0.0") - def registerEventListener(@Nonnull eventListener: EventListener): Future[Unit] = - baker.registerEventListener((recipeEventMetadata: scaladsl.RecipeEventMetadata, event: String) => - eventListener.processEvent(recipeEventMetadata.recipeInstanceId, event)) - - /** - * Registers a listener that listens to all Baker events - * - * @param listenerFunction - * @return - */ - override def registerBakerEventListener(@Nonnull listenerFunction: Consumer[BakerEvent]): CompletableFuture[Unit] = - toCompletableFuture(baker.registerBakerEventListener((event: scaladsl.BakerEvent) => listenerFunction.accept(event.asJava))) - - - /** - * Returns the visual state of the recipe in dot format with a default timeout of 20 seconds - * - * @param recipeInstanceId The process identifier - * @return - */ - def getVisualState(@Nonnull recipeInstanceId: String): CompletableFuture[String] = - toCompletableFuture(baker.getVisualState(recipeInstanceId, RecipeVisualStyle.default)) - - /** - * Returns the visual state of the recipe in dot format with a default timeout of 20 seconds - * - * @param recipeInstanceId The process identifier - * @return - */ - def getVisualState(@Nonnull recipeInstanceId: String, @Nonnull style: RecipeVisualStyle): CompletableFuture[String] = - toCompletableFuture(baker.getVisualState(recipeInstanceId, style)) - - @nowarn - private def toCompletableFuture[T](@Nonnull scalaFuture: Future[T]): CompletableFuture[T] = - scalaFuture.asJava.toCompletableFuture - - @nowarn - private def toCompletableFutureMap[K, V](@Nonnull scalaFuture: Future[Map[K, V]]): CompletableFuture[java.util.Map[K, V]] = - scalaFuture.asJava - .toCompletableFuture - .thenApply(_.asJava) - - /** - * This method is used to add metadata to your request. This will be added to the ingredients map in Baker. - * Since this is meant to be used as metadata this should not - * These cannot be ingredients already found in your recipe. - * - * @param metadata - */ - override def addMetaData(recipeInstanceId: String, metadata: java.util.Map[String, String]): CompletableFuture[Unit] = { - val x: Map[String, String] = metadata.asScala.toMap - toCompletableFuture(baker.addMetaData(recipeInstanceId, x)) - } - - def fireSensoryEventAndAwaitReceived(recipeInstanceId: String, event: EventInstance): CompletableFuture[SensoryEventStatus] = - fireSensoryEventAndAwaitReceived(recipeInstanceId, event, Optional.empty()) - - override def fireSensoryEventAndAwaitReceived(recipeInstanceId: String, event: EventInstance, correlationId: Optional[String]): CompletableFuture[SensoryEventStatus] = - toCompletableFuture(baker.fireSensoryEventAndAwaitReceived(recipeInstanceId, event.asScala, Option.apply(correlationId.orElse(null)))) - - /** - * Convenience overload for Java callers that don't want to specify waitForNext. - */ - def awaitEvent(recipeInstanceId: String, eventName: String, timeout: Duration): CompletableFuture[Unit] = - awaitEvent(recipeInstanceId, eventName, timeout, false) - - override def awaitEvent(recipeInstanceId: String, eventName: String, timeout: Duration, waitForNext: Boolean): CompletableFuture[Unit] = - toCompletableFuture(baker.awaitEvent(recipeInstanceId, eventName, FiniteDuration.apply(timeout.toMillis, TimeUnit.MILLISECONDS), waitForNext)) - - override def awaitCompleted(recipeInstanceId: String, timeout: Duration): CompletableFuture[SensoryEventStatus] = - toCompletableFuture(baker.awaitCompleted(recipeInstanceId, FiniteDuration.apply(timeout.toMillis, TimeUnit.MILLISECONDS))) -} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceConfig.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceConfig.scala deleted file mode 100644 index 526089e86..000000000 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceConfig.scala +++ /dev/null @@ -1,24 +0,0 @@ -package com.ing.baker.runtime.javadsl - -import com.ing.baker.runtime.model.recipeinstance.RecipeInstance - -import java.time.Duration -import java.util.Optional -import scala.concurrent.duration.{FiniteDuration, NANOSECONDS} -import scala.jdk.CollectionConverters.CollectionHasAsScala -import scala.jdk.OptionConverters.RichOptional - -case class RecipeInstanceConfig(idleTTL: Optional[Duration] = Optional.of(Duration.ofSeconds(5)), - ingredientsFilter: java.util.List[String] = java.util.List.of()) { - - def withIdleTTL(idleTTL: Optional[Duration]) = copy(idleTTL = idleTTL) - - def withIngredientsFilter(ingredientsFilter: java.util.List[String]) = ingredientsFilter - - def toBakerFRecipeInstanceConfig(): RecipeInstance.Config = { - RecipeInstance.Config( - idleTTL.toScala.map(duration => FiniteDuration.apply(duration.toNanos, NANOSECONDS)), - ingredientsFilter.asScala.toSeq - ) - } -} diff --git a/core/baker-runtime-in-memory/pom.xml b/core/baker-runtime-in-memory/pom.xml new file mode 100644 index 000000000..04f48b571 --- /dev/null +++ b/core/baker-runtime-in-memory/pom.xml @@ -0,0 +1,240 @@ + + + 4.0.0 + + + com.ing.baker + baker + 5.1.0-SNAPSHOT + ../../pom.xml + + + baker-runtime-in-memory + Baker Runtime In-Memory + In-Memory Baker runtime definitions and core API + + + + com.ing.baker + baker-runtime + ${project.version} + + + com.ing.baker + baker-intermediate-language + ${project.version} + + + com.ing.baker + baker-annotations + ${project.version} + + + + org.scala-lang + scala-library + + + + + org.typelevel + cats-effect_${scala.compat.version} + + + co.fs2 + fs2-core_${scala.compat.version} + + + co.fs2 + fs2-io_${scala.compat.version} + + + + + com.thesamet.scalapb + scalapb-runtime_${scala.compat.version} + + + + + org.slf4j + slf4j-api + + + + + com.google.code.findbugs + jsr305 + provided + + + + + com.ing.baker + baker-recipe-dsl + ${project.version} + test + + + com.ing.baker + baker-recipe-dsl + ${project.version} + test-jar + test + + + com.ing.baker + baker-compiler + ${project.version} + test + + + com.ing.baker + baker-types + ${project.version} + test-jar + test + + + com.ing.baker + baker-runtime + ${project.version} + test-jar + test + + + junit + junit + test + + + org.scalatest + scalatest_${scala.compat.version} + test + + + org.scalatestplus + mockito-3-12_${scala.compat.version} + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin.version} + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + + + src/main/kotlin + + + org.codehaus.mojo + build-helper-maven-plugin + ${build.helper.maven.plugin.version} + + + add-test-source + generate-test-sources + + add-test-source + + + + src/test/kotlin + + + + + + + + net.alchim31.maven + scala-maven-plugin + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + ${basedir}/src/main/protobuf + ${basedir}/target/generated-sources/protobuf/scala + false + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + + + scalapb + com.thesamet.scalapb + compilerplugin_${scala.compat.version} + ${scalapb.version} + scalapb.ScalaPbCodeGenerator + + + + + + + compile + + + + + + + org.scalatest + scalatest-maven-plugin + + -XX:+EnableDynamicAgentLoading + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + src/main/kotlin + src/main/scala + + + + + test-compile + test-compile + + test-compile + + + + src/test/kotlin + src/test/scala + + + + + + 1.8 + + + + + diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt new file mode 100644 index 000000000..a570b3b8e --- /dev/null +++ b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt @@ -0,0 +1,115 @@ +package com.ing.baker.runtime.inmemory + +import cats.arrow.FunctionK +import cats.effect.IO +import cats.effect.kernel.Async +import cats.effect.unsafe.IORuntime +import com.ing.baker.runtime.model.BakerComponents +import com.ing.baker.runtime.model.BakerConfig +import com.ing.baker.runtime.model.BakerF +import com.ing.baker.runtime.model.BakerLogging +import com.ing.baker.runtime.model.InteractionInstance +import scala.concurrent.Future +import scala.jdk.javaapi.CollectionConverters.asScala +import scala.reflect.`ClassTag$` +import com.ing.baker.runtime.defaultinteractions.`package$`.`MODULE$` as defaultinteractions +import com.ing.baker.runtime.javadsl.Baker as JavaBaker +import com.ing.baker.runtime.javadsl.InteractionInstance as JavaInteractionInstance +import scala.collection.immutable.List as ScalaList + +/** + * In-memory implementation of Baker. + * + * Note: This class may show compiler warnings due to Scala/Kotlin interoperability limitations. + * The compiler may report unimplemented abstract members or type mismatches, but these are false positives. + * All required functionality is properly implemented in the parent BakerF class. + */ +@Suppress("ABSTRACT_MEMBER_NOT_IMPLEMENTED") +class InMemoryBaker( + private val bakerConfig: BakerConfig, + components: BakerComponents> +) : BakerF>(components, IO.asyncForIO() as Async>, IO.asyncForIO() as Async>) { + + companion object { + + @JvmStatic + fun build(implementations: ScalaList<*>): IO>> = build(BakerConfig.default(), implementations) + + @JvmStatic + @Suppress("UNCHECKED_CAST") + fun build( + config: BakerConfig, + implementations: ScalaList<*> + ): IO>> { + val recipeInstanceManager = + InMemoryRecipeInstanceManager( + config.retentionPeriodCheckInterval(), + config.idleTimeout() + ) + val interactionInstances = + implementations.concat(defaultinteractions.all()) as ScalaList>> + val recipeManager = InMemoryRecipeManager() + val eventStream = InMemoryEventStream() + val interactions = InMemoryInteractionManager(interactionInstances) + val components = BakerComponents( + interactions, + recipeInstanceManager, + recipeManager, + eventStream, + BakerLogging.default() + ) + return IO.pure( + InMemoryBaker(config, components as BakerComponents>) as BakerF> + ) + } + + @JvmStatic + fun java(config: BakerConfig, implementations: List): JavaBaker { + val futureToIO = object : FunctionK, IO<*>> { + override fun apply(fa: Future<*>): IO<*> = IO.fromFuture(IO.pure(fa)) + } + + val scalaInteractions = + implementations + .map { item -> + when (item) { + is InteractionInstance<*> -> item + is JavaInteractionInstance -> item.asScala() + .translate(futureToIO) as InteractionInstance> + + else -> InteractionInstance.unsafeFrom( + item, + IO.asyncForIO(), + `ClassTag$`.`MODULE$`.apply(IO::class.java) + ) + } + } + .let { asScala(it) } + .toList() + + val ioToFuture = object : FunctionK, Future<*>> { + override fun apply(fa: IO<*>): Future<*> = fa.unsafeToFuture(IORuntime.global()) + } + + return build(config, scalaInteractions) + .unsafeRunSync(IORuntime.global()) + .asDeprecatedFutureImplementation(ioToFuture, futureToIO) + .let { JavaBaker(it) } + } + + @JvmStatic + fun java(implementations: List): JavaBaker = + java(BakerConfig.default(), implementations) + + @JvmStatic + fun java(): JavaBaker = + java(BakerConfig.default(), emptyList()) + } + + override fun config(): BakerConfig = bakerConfig + + /** + * Attempts to gracefully shutdown the baker system. + */ + override fun gracefulShutdown(): IO = IO.pure {} +} diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt new file mode 100644 index 000000000..0545fe85a --- /dev/null +++ b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt @@ -0,0 +1,23 @@ +package com.ing.baker.runtime.inmemory + +import cats.effect.IO +import com.ing.baker.runtime.model.EventStream +import com.ing.baker.runtime.scaladsl.BakerEvent +import scala.Function1 +import scala.jdk.CollectionConverters.CollectionHasAsScala +import scala.runtime.BoxedUnit +import java.util.concurrent.ConcurrentLinkedQueue +import scala.collection.immutable.List as ScalaList + +class InMemoryEventStream : EventStream> { + + private val store: ConcurrentLinkedQueue> = ConcurrentLinkedQueue() + + override fun fetchListeners(): IO>> = + IO.pure( + ScalaList.from(CollectionHasAsScala(store).asScala()) as ScalaList> + ) + + override fun subscribe(listenerFunction: Function1): IO = + IO.pure(store.add(listenerFunction)).map { BoxedUnit.UNIT } +} diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt new file mode 100644 index 000000000..0ee281a59 --- /dev/null +++ b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt @@ -0,0 +1,13 @@ +package com.ing.baker.runtime.inmemory + +import cats.effect.IO +import com.ing.baker.runtime.model.InteractionInstance +import com.ing.baker.runtime.model.InteractionManager +import scala.collection.immutable.List as ScalaList + +class InMemoryInteractionManager(private val implementations: ScalaList>>): InteractionManager> { + + override fun allowSupersetForOutputTypes(): Boolean = false + + override fun listAll(): IO>>> = IO.pure(implementations) +} diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt new file mode 100644 index 000000000..3a2988e72 --- /dev/null +++ b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt @@ -0,0 +1,161 @@ +package com.ing.baker.runtime.inmemory + +import cats.effect.IO +import cats.effect.kernel.Async +import cats.effect.unsafe.IORuntime +import com.ing.baker.runtime.model.BakerComponents +import com.ing.baker.runtime.model.RecipeInstanceManager +import com.ing.baker.runtime.model.RecipeInstanceStatus +import com.ing.baker.runtime.model.RecipeInstanceStatus.Active +import com.ing.baker.runtime.model.RecipeInstanceStatus.Deleted +import com.ing.baker.runtime.model.recipeinstance.RecipeInstance +import com.ing.baker.runtime.model.recipeinstance.RecipeInstanceState +import com.ing.baker.runtime.scaladsl.RecipeInstanceMetadata +import org.slf4j.LoggerFactory +import scala.Option +import scala.jdk.CollectionConverters.MapHasAsScala +import scala.jdk.javaapi.DurationConverters.toScala +import scala.runtime.BoxedUnit +import java.time.Duration +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit +import scala.collection.immutable.Map as ScalaMap +import scala.collection.immutable.`Map$`.`MODULE$` as ScalaMapObject +import scala.collection.immutable.Set as ScalaSet + +/** + * Kotlin implementation of InMemoryRecipeInstanceManager. + * + * This class manages recipe instances in memory using a ConcurrentHashMap for thread-safe storage. + * It periodically cleans up idle recipe instances based on the configured idle timeout. + */ +class InMemoryRecipeInstanceManager( + retentionPeriodCheckInterval: Duration, + private val idleTimeOut: Duration +) : RecipeInstanceManager>, AutoCloseable { + + private val logger = LoggerFactory.getLogger(javaClass.name) + + private val store = ConcurrentHashMap>>() + private val scheduler: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor() + + init { + // Start periodic cleanup task + scheduler.scheduleAtFixedRate( + { + try { + @Suppress("UNCHECKED_CAST") + cleanupRecipeInstances( + toScala(idleTimeOut), + IO.asyncForIO() as Async> + ).unsafeRunSync(IORuntime.global()) + } catch (e: Exception) { + // Log error but don't stop the scheduler + // Errors are expected and handled silently to avoid stopping the cleanup task + logger.warn("Error during cleanup of idle recipe instances: $e") + } + }, + retentionPeriodCheckInterval.toMillis(), + retentionPeriodCheckInterval.toMillis(), + TimeUnit.MILLISECONDS + ) + } + + @Suppress("UNCHECKED_CAST") + override fun fetch(recipeInstanceId: String): IO>>> = + IO.delay { + Option.apply( + store.compute(recipeInstanceId) { _, status -> + when (status) { + is Active<*> -> { + Active( + status.recipeInstance() as RecipeInstance>, + System.currentTimeMillis() + ) + } + + else -> status + } + } + ) + } as IO>>> + + override fun store(recipeInstance: RecipeInstance>, components: BakerComponents>): IO = + IO.delay { + val status = Active( + recipeInstance, + System.currentTimeMillis() + ) as RecipeInstanceStatus> + store[recipeInstance.recipeInstanceId()] = status + }.map { BoxedUnit.UNIT } + + override fun idleStop(recipeInstanceId: String): IO = IO.unit() + + @Suppress("UNCHECKED_CAST") + override fun getAllRecipeInstancesMetadata(): IO> = + IO.defer { + store.entries.map { (recipeInstanceId, status) -> + when (status) { + is Active<*> -> { + val recipeInstance = status.recipeInstance() as RecipeInstance> + recipeInstance.state().get().map { currentState -> + RecipeInstanceMetadata( + (currentState as RecipeInstanceState<*>).recipe().recipeId(), + recipeInstanceId, + currentState.createdOn() + ) + } + } + + is Deleted<*> -> { + IO.pure( + RecipeInstanceMetadata( + status.recipeId(), + recipeInstanceId, + status.createdOn() + ) + ) + } + } + }.fold(IO.pure(emptyList())) { acc, io -> + // Sequence all IO operations and convert to Set + acc.flatMap { list -> + (io as IO).map { metadata: RecipeInstanceMetadata -> list + metadata } + } + }.map { list: List -> + scala.collection.immutable.`Set$`.`MODULE$`.from( + scala.jdk.CollectionConverters.IterableHasAsScala(list).asScala() + ) as ScalaSet + } + } as IO> + + override fun fetchAll(): IO>>> = + IO.delay { + ScalaMapObject.from(MapHasAsScala(store).asScala()) + as ScalaMap>> + } as IO>>> + + override fun remove(recipeInstanceId: String): IO = + idleStop(recipeInstanceId).flatMap { + IO.delay { + store.remove(recipeInstanceId) + } + }.map { BoxedUnit.UNIT } + + /** + * Gracefully shutdown the cleanup scheduler. + * Call this method when the instance manager is no longer needed to release resources. + */ + override fun close() { + scheduler.shutdown() + try { + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + scheduler.shutdownNow() + } + } catch (_: InterruptedException) { + scheduler.shutdownNow() + } + } +} diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt new file mode 100644 index 000000000..39080e9bc --- /dev/null +++ b/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt @@ -0,0 +1,34 @@ +package com.ing.baker.runtime.inmemory + +import cats.effect.IO +import com.ing.baker.il.CompiledRecipe +import com.ing.baker.runtime.common.RecipeRecord +import com.ing.baker.runtime.model.RecipeManager +import com.typesafe.scalalogging.Logger +import org.slf4j.LoggerFactory +import scala.Option +import scala.jdk.CollectionConverters.MapHasAsScala +import java.util.concurrent.ConcurrentHashMap +import scala.collection.Map as ScalaMap + +class InMemoryRecipeManager : RecipeManager> { + + private val store = ConcurrentHashMap() + + override fun logger(): Logger = Logger(LoggerFactory.getLogger(javaClass.name)) + + @Suppress("UNCHECKED_CAST") + override fun store(compiledRecipe: CompiledRecipe, timestamp: Long): IO = + store.put( + compiledRecipe.recipeId(), + RecipeRecord.of(compiledRecipe, timestamp, true, true) + ).let { IO.unit() as IO } + + override fun fetch(recipeId: String): IO> = + IO.pure(Option.apply(store[recipeId])) + + override fun fetchAll(): IO> = + IO.pure( + ScalaMap.from(MapHasAsScala(store).asScala()) as ScalaMap + ) +} diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala b/core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala similarity index 55% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala rename to core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala index d51a480fc..7af941f27 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala +++ b/core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala @@ -1,11 +1,12 @@ package com.ing.baker.runtime.inmemory import cats.effect.{IO, Resource} -import com.ing.baker.runtime.model.{BakerF, BakerModelSpec, InteractionInstance} +import com.ing.baker.runtime.model.{BakerConfig, BakerF, BakerModelSpec, InteractionInstance} import scala.concurrent.duration._ +import scala.jdk.DurationConverters._ -class InMemoryBakerSpec extends BakerModelSpec{ +class InMemoryBakerSpec extends BakerModelSpec { /** This will execute the predefined baker tests from BakerModelSpec */ runAll() @@ -15,11 +16,8 @@ class InMemoryBakerSpec extends BakerModelSpec{ _ <- Resource.eval(IO.unit) baker = InMemoryBaker.java() // for coverage _ = baker.gracefulShutdown() - } yield Context((interactions: List[InteractionInstance[IO]]) => InMemoryBaker.build(BakerF.Config( - bakeTimeout = 10.seconds, - processEventTimeout = 10.seconds, - inquireTimeout = 10.seconds, - shutdownTimeout = 10.seconds, - addRecipeTimeout = 10.seconds - ), interactions)) + } yield Context((interactions: List[InteractionInstance[IO]]) => InMemoryBaker.build( + BakerConfig.default().withInquireTimeout(10.seconds.toJava), + interactions + ).asInstanceOf[IO[BakerF[IO]]]) } diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala b/core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala similarity index 70% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala rename to core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala index c2e7985a4..fb67b054e 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala +++ b/core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala @@ -1,6 +1,6 @@ package com.ing.baker.runtime.inmemory -import cats.effect.IO +import cats.effect.{Async, IO} import cats.effect.unsafe.implicits.global import com.ing.baker.compiler.RecipeCompiler import com.ing.baker.recipe.TestRecipe @@ -8,37 +8,50 @@ import com.ing.baker.recipe.TestRecipe._ import com.ing.baker.recipe.common.InteractionFailureStrategy import com.ing.baker.recipe.scaladsl.Recipe import com.ing.baker.runtime.common.BakerException.NoSuchProcessException -import com.ing.baker.runtime.model.{BakerF, InteractionInstance} +import com.ing.baker.runtime.model.{BakerConfig, BakerF, InteractionInstance} import com.ing.baker.runtime.scaladsl.{EventInstance, RecipeInstanceState} -import org.scalatest.Retries +import org.scalatest.{Outcome, Retries} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import org.scalatest.tagobjects.Retryable import java.util.UUID import scala.concurrent.duration._ -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.Future +import scala.jdk.DurationConverters._ +import scala.reflect.ClassTag class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { - override def withFixture(test: NoArgTest) = { - if (isRetryable(test)) - withRetry { super.withFixture(test) } - else + // Implicit instances needed for InteractionInstance.unsafeFrom[IO] + implicit val asyncIO: Async[IO] = IO.asyncForIO + implicit val classTagIO: ClassTag[IO[Any]] = ClassTag(classOf[IO[_]]) + + override def withFixture(test: NoArgTest): Outcome = { + if (isRetryable(test)) + withRetry { super.withFixture(test) + } + else + super.withFixture(test) } + private def buildBaker(config: BakerConfig, interactions: List[InteractionInstance[IO]]): IO[BakerF[IO]] = + InMemoryBaker.build(config, interactions).asInstanceOf[IO[BakerF[IO]]] + behavior of "InMemoryRecipeInstanceManager" it should "find a process in the timeout" in { val recipeInstanceId = UUID.randomUUID().toString val result: IO[RecipeInstanceState] = for { - baker <- InMemoryBaker.build(BakerF.Config(idleTimeout = 100.milliseconds, allowAddingRecipeWithoutRequiringInstances = true), List.empty) + baker <- buildBaker(BakerConfig.default() + .withIdleTimeout(100.milliseconds.toJava) + .withAllowAddingRecipeWithoutRequiringInstances(true), List.empty) recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(TestRecipe.getRecipe("InMemory")), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) result <- baker.getRecipeInstanceState(recipeInstanceId) - } yield (result) + } yield result result.unsafeRunSync() } @@ -50,31 +63,30 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { .withSensoryEvents(initialEvent) .withRetentionPeriod(100 milliseconds) - class InteractionOneInterfaceImplementation() extends TestRecipe.InteractionOne { + class InteractionOneInterfaceImplementation extends TestRecipe.InteractionOne { override def apply(recipeInstanceId: String, initialIngredient: String): Future[InteractionOneSuccessful] = { - println("Interaction executing") - Future.successful(new InteractionOneSuccessful("output")) + Future.successful(InteractionOneSuccessful("output")) } } val recipeInstanceId = UUID.randomUUID().toString val result: IO[RecipeInstanceState] = for { - baker <- InMemoryBaker.build(BakerF.Config( - idleTimeout = 10.milliseconds, - retentionPeriodCheckInterval = 10.milliseconds, - allowAddingRecipeWithoutRequiringInstances = true), - List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) + baker <- buildBaker(BakerConfig.default() + .withIdleTimeout(10.milliseconds.toJava) + .withRetentionPeriodCheckInterval(10.milliseconds.toJava) + .withAllowAddingRecipeWithoutRequiringInstances(true), + List(InteractionInstance.unsafeFrom[IO](new InteractionOneInterfaceImplementation()))) recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) result <- baker.getRecipeInstanceState(recipeInstanceId) - } yield (result) + } yield result assertThrows[NoSuchProcessException](result.unsafeRunSync()) } - it should "delete a process after the idleTimeOut if the process is inactive" taggedAs(Retryable) in { + it should "delete a process after the idleTimeOut if the process is inactive" taggedAs Retryable in { val recipe = Recipe("tempRecipe2") .withInteractions( interactionOne @@ -83,24 +95,24 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { class InteractionOneInterfaceImplementation() extends TestRecipe.InteractionOne { override def apply(recipeInstanceId: String, initialIngredient: String): Future[InteractionOneSuccessful] = { - Future.successful(new InteractionOneSuccessful("output")) + Future.successful(InteractionOneSuccessful("output")) } } val recipeInstanceId = UUID.randomUUID().toString val result: IO[RecipeInstanceState] = for { - baker <- InMemoryBaker.build(BakerF.Config( - idleTimeout = 100.milliseconds, - retentionPeriodCheckInterval = 10.milliseconds, - allowAddingRecipeWithoutRequiringInstances = true), - List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) + baker <- buildBaker(BakerConfig.default() + .withIdleTimeout(100.milliseconds.toJava) + .withRetentionPeriodCheckInterval(10.milliseconds.toJava) + .withAllowAddingRecipeWithoutRequiringInstances(true), + List(InteractionInstance.unsafeFrom[IO](new InteractionOneInterfaceImplementation()))) recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(200.milliseconds) result <- baker.getRecipeInstanceState(recipeInstanceId) - } yield (result) + } yield result assertThrows[NoSuchProcessException](result.unsafeRunSync()) } @@ -122,21 +134,21 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { val recipeInstanceId = UUID.randomUUID().toString val result: IO[RecipeInstanceState] = for { - baker <- InMemoryBaker.build(BakerF.Config( - idleTimeout = 100.milliseconds, - retentionPeriodCheckInterval = 10.milliseconds, - allowAddingRecipeWithoutRequiringInstances = true), - List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) + baker <- buildBaker(BakerConfig.default() + .withIdleTimeout(100.milliseconds.toJava) + .withRetentionPeriodCheckInterval(10.milliseconds.toJava) + .withAllowAddingRecipeWithoutRequiringInstances(true), + List(InteractionInstance.unsafeFrom[IO](new InteractionOneInterfaceImplementation()))) recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) result <- baker.getRecipeInstanceState(recipeInstanceId) - } yield (result) + } yield result result.unsafeRunSync() } - it should "not delete a process if the idle timeout is reset due to activity" taggedAs(Retryable) in { + it should "not delete a process if the idle timeout is reset due to activity" taggedAs Retryable in { val recipe = Recipe("tempRecipe3") .withInteractions( interactionOne @@ -152,11 +164,11 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { val recipeInstanceId = UUID.randomUUID().toString val result: IO[RecipeInstanceState] = for { - baker <- InMemoryBaker.build(BakerF.Config( - idleTimeout = 100.milliseconds, - retentionPeriodCheckInterval = 10.milliseconds, - allowAddingRecipeWithoutRequiringInstances = true), - List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) + baker <- buildBaker(BakerConfig.default() + .withIdleTimeout(100.milliseconds.toJava) + .withRetentionPeriodCheckInterval(10.milliseconds.toJava) + .withAllowAddingRecipeWithoutRequiringInstances(true), + List(InteractionInstance.unsafeFrom[IO](new InteractionOneInterfaceImplementation()))) recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() @@ -164,7 +176,7 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(80.milliseconds) result <- baker.getRecipeInstanceState(recipeInstanceId) - } yield (result) + } yield result result.unsafeRunSync() } @@ -187,18 +199,17 @@ class InMemoryMemoryCleanupSpec extends AnyFlatSpec with Matchers with Retries { val recipeInstanceId = UUID.randomUUID().toString val result: IO[RecipeInstanceState] = for { - baker <- InMemoryBaker.build(BakerF.Config( - idleTimeout = 100.milliseconds, - retentionPeriodCheckInterval = 10.milliseconds, - allowAddingRecipeWithoutRequiringInstances = true), - List(InteractionInstance.unsafeFrom(new InteractionOneInterfaceImplementation()))) + baker <- buildBaker(BakerConfig.default() + .withIdleTimeout(100.milliseconds.toJava) + .withRetentionPeriodCheckInterval(10.milliseconds.toJava) + .withAllowAddingRecipeWithoutRequiringInstances(true), + List(InteractionInstance.unsafeFrom[IO](new InteractionOneInterfaceImplementation()))) recipeId <- baker.addRecipe(RecipeCompiler.compileRecipe(recipe), validate = false) _ <- baker.bake(recipeId, recipeInstanceId) _ = baker.fireEventAndResolveWhenCompleted(recipeInstanceId, EventInstance.unsafeFrom(InitialEvent("initialIngredient"))).unsafeRunAndForget() _ <- IO.sleep(120.milliseconds) result <- baker.getRecipeInstanceState(recipeInstanceId) - } yield (result) + } yield result assertThrows[NoSuchProcessException](result.unsafeRunSync()) } } - diff --git a/core/baker-interface-kotlin/pom.xml b/core/baker-runtime-kotlin/pom.xml similarity index 90% rename from core/baker-interface-kotlin/pom.xml rename to core/baker-runtime-kotlin/pom.xml index a943b7618..6f742738b 100644 --- a/core/baker-interface-kotlin/pom.xml +++ b/core/baker-runtime-kotlin/pom.xml @@ -11,14 +11,19 @@ ../../pom.xml - baker-interface-kotlin - Baker Interface Kotlin - Kotlin interface for Baker + baker-runtime-kotlin + Baker Runtime Kotlin + Kotlin runtime for Baker com.ing.baker - baker-interface + baker-runtime + ${project.version} + + + com.ing.baker + baker-runtime-in-memory ${project.version} diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt b/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt similarity index 100% rename from core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt rename to core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt b/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt similarity index 100% rename from core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt rename to core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt b/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt similarity index 100% rename from core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt rename to core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt b/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt similarity index 100% rename from core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt rename to core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt diff --git a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt b/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt similarity index 75% rename from core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt rename to core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt index 9c66f0975..5830974f3 100644 --- a/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt +++ b/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt @@ -1,6 +1,7 @@ package com.ing.baker.runtime.kotlindsl import com.ing.baker.runtime.inmemory.InMemoryBaker +import com.ing.baker.runtime.model.BakerConfig object InMemoryBaker { fun kotlin(implementations: List = emptyList()) = Baker(InMemoryBaker.java(implementations)) @@ -8,7 +9,7 @@ object InMemoryBaker { /** * Creates a InMemoryBaker with the com.ing.baker.runtime.kotlindsl.InMemoryBaker.Config. */ - fun kotlin(config: Config, - implementations: List = emptyList()) = Baker(InMemoryBaker.java(config.toBakerFConfig(), implementations)) + fun kotlin(config: BakerConfig, + implementations: List = emptyList()) = Baker(InMemoryBaker.java(config, implementations)) } diff --git a/core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt b/core/baker-runtime-kotlin/src/test/kotlin/BakerInterfaceTest.kt similarity index 100% rename from core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt rename to core/baker-runtime-kotlin/src/test/kotlin/BakerInterfaceTest.kt diff --git a/core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt b/core/baker-runtime-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt similarity index 100% rename from core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt rename to core/baker-runtime-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt diff --git a/core/baker-interface/pom.xml b/core/baker-runtime/pom.xml similarity index 90% rename from core/baker-interface/pom.xml rename to core/baker-runtime/pom.xml index 35c18d096..187ee91bb 100644 --- a/core/baker-interface/pom.xml +++ b/core/baker-runtime/pom.xml @@ -11,9 +11,9 @@ ../../pom.xml - baker-interface - Baker Interface - Baker interface definitions and core API + baker-runtime + Baker Runtime + Baker runtime definitions and core API @@ -49,6 +49,7 @@ circe-generic-extras_${scala.compat.version} + org.typelevel cats-effect_${scala.compat.version} @@ -61,6 +62,7 @@ co.fs2 fs2-io_${scala.compat.version} + javax.inject javax.inject @@ -181,6 +183,19 @@ -XX:+EnableDynamicAgentLoading + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + + test-jar + + + + diff --git a/core/baker-interface/src/main/protobuf/common.proto b/core/baker-runtime/src/main/protobuf/common.proto similarity index 100% rename from core/baker-interface/src/main/protobuf/common.proto rename to core/baker-runtime/src/main/protobuf/common.proto diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Baker.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Baker.scala similarity index 91% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Baker.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Baker.scala index 2752fae84..24a675cbc 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Baker.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Baker.scala @@ -39,6 +39,8 @@ trait Baker[F[_]] extends LanguageApi { type InteractionExecutionResultType <: InteractionExecutionResult { type Language <: self.Language } type DurationType + + type UnitType /** * Adds a recipe to baker and returns a recipeId for the recipe. @@ -97,7 +99,7 @@ trait Baker[F[_]] extends LanguageApi { * @param recipeInstanceId The identifier for the newly baked process * @return */ - def bake(recipeId: String, recipeInstanceId: String): F[Unit] + def bake(recipeId: String, recipeInstanceId: String): F[UnitType] /** @@ -106,10 +108,10 @@ trait Baker[F[_]] extends LanguageApi { * This is similar to calling addMetaData after doing the regular bake but depending on the implementation this can be more optimized. * @param recipeId The recipeId for the recipe to bake * @param recipeInstanceId The identifier for the newly baked process - * @param metadata + * @param metadata Metadata * @return */ - def bake(recipeId: String, recipeInstanceId: String, metadata: language.Map[String, String]): F[Unit] + def bake(recipeId: String, recipeInstanceId: String, metadata: language.Map[String, String]): F[UnitType] /** * Deletes a recipeInstance. Once deleted the instance will be marked as `Deleted` in the index and then removed after a while. @@ -118,7 +120,7 @@ trait Baker[F[_]] extends LanguageApi { * @param removeFromIndex If enabled removes all references to the id directly * @return */ - def deleteRecipeInstance(recipeInstanceId: String, removeFromIndex: Boolean): F[Unit] + def deleteRecipeInstance(recipeInstanceId: String, removeFromIndex: Boolean): F[UnitType] /** * Fires a sensory event for a given recipe instance and waits until it has been accepted and persisted. @@ -133,13 +135,13 @@ trait Baker[F[_]] extends LanguageApi { * systems. If a second event with the same ID arrives, the method will fail with * [[AlreadyReceivedException]]. * @return A future that resolves to [[SensoryEventStatus.Received]] when the event has been successfully persisted. - * @throws NoSuchProcessException if the recipe instance does not exist. - * @throws ProcessDeletedException if the recipe instance has been deleted. - * @throws InvalidEventException if the event is not a valid sensory event for the recipe. - * @throws AlreadyReceivedException if an event with the same `correlationId` has already been received. - * @throws java.util.concurrent.TimeoutException if the request does not complete within the configured `baker.process-event-timeout`. - * Note that a timeout does NOT mean the event was not processed; it may still be - * processed later. + * @throws com.ing.baker.runtime.common.BakerException.NoSuchProcessException if the recipe instance does not exist. + * @throws com.ing.baker.runtime.common.BakerException.ProcessDeletedException if the recipe instance has been deleted. + * @throws InvalidEventException if the event is not a valid sensory event for the recipe. + * @throws AlreadyReceivedException if an event with the same `correlationId` has already been received. + * @throws java.util.concurrent.TimeoutException if the request does not complete within the configured `baker.process-event-timeout`. + * Note that a timeout does NOT mean the event was not processed; it may still be + * processed later. */ def fireSensoryEventAndAwaitReceived(recipeInstanceId: String, event: EventInstanceType, correlationId: language.Option[String]): F[SensoryEventStatus] @@ -161,7 +163,7 @@ trait Baker[F[_]] extends LanguageApi { * @throws ProcessDeletedException if the recipe instance has been deleted. * @throws TimeoutException if the event does not occur within the specified `timeout`. */ - def awaitEvent(recipeInstanceId: String, eventName: String, timeout: DurationType, waitForNext: Boolean = false): F[Unit] + def awaitEvent(recipeInstanceId: String, eventName: String, timeout: DurationType, waitForNext: Boolean = false): F[UnitType] /** * Waits until a recipe instance completes execution. @@ -220,7 +222,7 @@ trait Baker[F[_]] extends LanguageApi { * These cannot be ingredients already found in your recipe. * @param metadata */ - def addMetaData(recipeInstanceId: String, metadata: language.Map[String, String]): F[Unit] + def addMetaData(recipeInstanceId: String, metadata: language.Map[String, String]): F[UnitType] /** * Returns an index of all running processes. @@ -296,14 +298,14 @@ trait Baker[F[_]] extends LanguageApi { * * Note that the delivery guarantee is *AT MOST ONCE*. Do not use it for critical functionality */ - def registerEventListener(recipeName: String, listenerFunction: language.BiConsumerFunction[RecipeMetadataType, String]): F[Unit] + def registerEventListener(recipeName: String, listenerFunction: language.BiConsumerFunction[RecipeMetadataType, String]): F[UnitType] /** * Registers a listener to all runtime events for all recipes that run in this Baker instance. * * Note that the delivery guarantee is *AT MOST ONCE*. Do not use it for critical functionality */ - def registerEventListener(listenerFunction: language.BiConsumerFunction[RecipeMetadataType, String]): F[Unit] + def registerEventListener(listenerFunction: language.BiConsumerFunction[RecipeMetadataType, String]): F[UnitType] /** * Registers a listener function that listens to all BakerEvents @@ -313,19 +315,19 @@ trait Baker[F[_]] extends LanguageApi { * @param listenerFunction * @return */ - def registerBakerEventListener(listenerFunction: language.ConsumerFunction[BakerEventType]): F[Unit] + def registerBakerEventListener(listenerFunction: language.ConsumerFunction[BakerEventType]): F[UnitType] /** * Attempts to gracefully shutdown the baker system. */ - def gracefulShutdown(): F[Unit] + def gracefulShutdown(): F[UnitType] /** * Retries a blocked interaction. * * @return */ - def retryInteraction(recipeInstanceId: String, interactionName: String): F[Unit] + def retryInteraction(recipeInstanceId: String, interactionName: String): F[UnitType] /** * Resolves a blocked interaction by specifying it's output. @@ -334,14 +336,14 @@ trait Baker[F[_]] extends LanguageApi { * * @return */ - def resolveInteraction(recipeInstanceId: String, interactionName: String, event: EventInstanceType): F[Unit] + def resolveInteraction(recipeInstanceId: String, interactionName: String, event: EventInstanceType): F[UnitType] /** * Stops the retrying of an interaction. * * @return */ - def stopRetryingInteraction(recipeInstanceId: String, interactionName: String): F[Unit] + def stopRetryingInteraction(recipeInstanceId: String, interactionName: String): F[UnitType] } case class RecipeRecord( diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerException.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerException.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerException.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerException.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RejectReason.java b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RejectReason.java similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RejectReason.java rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RejectReason.java diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Utils.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Utils.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Utils.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Utils.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/package.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/package.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/common/package.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/package.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala new file mode 100644 index 000000000..eb1b3afea --- /dev/null +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala @@ -0,0 +1,18 @@ +package com.ing.baker.runtime.defaultinteractions + +import cats.effect.Async +import cats.syntax.all._ + +import scala.concurrent.duration.FiniteDuration + +class TimerInteraction[F[_]](skipWait: Boolean)(implicit F: Async[F]) { + + class TimeWaited() + + def apply(WaitTime: FiniteDuration): F[TimeWaited] = { + if(skipWait) + F.pure(new TimeWaited) + else + F.sleep(WaitTime).as(new TimeWaited) + } +} diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala new file mode 100644 index 000000000..34883d4a0 --- /dev/null +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala @@ -0,0 +1,23 @@ +package com.ing.baker.runtime.defaultinteractions + +import java.time.Duration +import java.util.concurrent.TimeUnit + +import cats.effect.Async +import cats.syntax.all._ + +import scala.concurrent.duration.FiniteDuration + +class TimerInteractionJava[F[_]](skipWait: Boolean)(implicit F: Async[F]) { + + class TimeWaited() + + val name = "TimerInteraction" + + def apply(WaitTime: Duration): F[TimeWaited] = { + if(skipWait) + F.pure(new TimeWaited) + else + F.sleep(FiniteDuration.apply(WaitTime.toMillis, TimeUnit.MILLISECONDS)).as(new TimeWaited) + } +} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala similarity index 71% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala index afac96ae4..42b267118 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala @@ -10,7 +10,7 @@ package object defaultinteractions { val config = ConfigFactory.load() val skipWait = config.hasPath(path) && config.getBoolean(path) List( - InteractionInstance.unsafeFrom(new TimerInteraction(skipWait)), - InteractionInstance.unsafeFrom(new TimerInteractionJava(skipWait))) + InteractionInstance.unsafeFrom[IO](new TimerInteraction[IO](skipWait)), + InteractionInstance.unsafeFrom[IO](new TimerInteractionJava[IO](skipWait))) } } diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala new file mode 100644 index 000000000..f15cb507e --- /dev/null +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala @@ -0,0 +1,379 @@ +package com.ing.baker.runtime.javadsl + +import com.ing.baker.il.RecipeVisualStyle +import com.ing.baker.runtime.common.{RecipeRecord, SensoryEventStatus} +import com.ing.baker.runtime.scaladsl +import com.ing.baker.types.Value +import java.time.Duration + +import java.util +import java.util.Optional +import java.util.concurrent.{CompletableFuture, TimeUnit} +import java.util.function.{BiConsumer, Consumer} +import javax.annotation.Nonnull +import scala.annotation.nowarn +import scala.jdk.CollectionConverters._ +import scala.jdk.FutureConverters._ +import scala.concurrent.duration._ +import scala.concurrent.Future + +class Baker(private val baker: scaladsl.Baker) extends BakerCommon { + + /** + * Get underlying baker instance, which provides the scala api. + */ + def getScalaBaker: scaladsl.Baker = baker + + override def close(): Unit = + toCompletableFuture(baker.gracefulShutdown()).orTimeout(10, TimeUnit.SECONDS).join() + + /** + * Adds a recipe to baker and returns a recipeId for the recipe. + * + * This function is idempotent, if the same (equal) recipe was added earlier this will return the same recipeId. + * + * @param recipeRecord The RecipeRecord recipe. + * @return A recipe identifier. + */ + def addRecipe(@Nonnull recipeRecord: RecipeRecord): CompletableFuture[String] = + toCompletableFuture(baker.addRecipe(recipeRecord)) + + /** + * Attempts to gracefully shutdown the baker system. + */ + def gracefulShutdown(): CompletableFuture[Void] = + toCompletableFutureVoid(baker.gracefulShutdown()) + + /** + * This bakes (creates) a new process instance of the recipe. + * + * @param recipeId The recipe this instance will be baked for + * @param recipeInstanceId The process identifier + */ + def bake(@Nonnull recipeId: String, @Nonnull recipeInstanceId: String): CompletableFuture[Void] = + toCompletableFutureVoid(baker.bake(recipeId, recipeInstanceId)) + + /** + * Creates a process instance for the given recipeId with the given RecipeInstanceId as identifier + * This variant also gets a metadata map added on bake. + * This is similar to calling addMetaData after doing the regular bake but depending on the implementation this can be more optimized. + * + * @param recipeId The recipeId for the recipe to bake + * @param recipeInstanceId The identifier for the newly baked process + * @param metadata Metadata + * @return + */ + def bake(recipeId: String, recipeInstanceId: String, metadata: util.Map[String, String]): CompletableFuture[Void] = + toCompletableFutureVoid(baker.bake(recipeId, recipeInstanceId, metadata.asScala.toMap)) + + def deleteRecipeInstance(recipeInstanceId: String, removeFromIndex: Boolean): CompletableFuture[Void] = + toCompletableFutureVoid(baker.deleteRecipeInstance(recipeInstanceId, removeFromIndex)) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") + def fireEventAndResolveWhenReceived(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventStatus] = + toCompletableFuture(baker.fireEventAndResolveWhenReceived(recipeInstanceId, event.asScala, Option.apply(correlationId.orElse(null)))) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitCompleted.", "5.1.0") + @nowarn + def fireEventAndResolveWhenCompleted(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventResult] = + toCompletableFuture(baker.fireEventAndResolveWhenCompleted(recipeInstanceId, event.asScala, Option.apply(correlationId.orElse(null)))).thenApply { result => + SensoryEventResult( + sensoryEventStatus = result.sensoryEventStatus, + eventNames = result.eventNames.asJava, + ingredients = result.ingredients.asJava + ) + } + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitEvent.", "5.1.0") + @nowarn + def fireEventAndResolveOnEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull onEvent: String, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventResult] = + toCompletableFuture(baker.fireEventAndResolveOnEvent(recipeInstanceId, event.asScala, onEvent, Option.apply(correlationId.orElse(null)))).thenApply { result => + SensoryEventResult( + sensoryEventStatus = result.sensoryEventStatus, + eventNames = result.eventNames.asJava, + ingredients = result.ingredients.asJava + ) + } + + @Deprecated + @deprecated("This method uses a callback-style API that is deprecated and will be removed after December 1st, 2026. Please use the new composable API: fireSensoryEventAndAwaitReceived followed by awaitCompleted or awaitEvent.", "5.1.0") + @nowarn + def fireEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): EventResolutions = { + val scalaResult = baker.fireEvent(recipeInstanceId, event.asScala) + EventResolutions( + resolveWhenReceived = toCompletableFuture(scalaResult.resolveWhenReceived), + resolveWhenCompleted = toCompletableFuture(scalaResult.resolveWhenCompleted).thenApply { result => + SensoryEventResult( + sensoryEventStatus = result.sensoryEventStatus, + eventNames = result.eventNames.asJava, + ingredients = result.ingredients.asJava + ) + }) + } + + /** + * Retries a blocked interaction. + * + * @param recipeInstanceId The process identifier. + * @param interactionName The name of the blocked interaction. + * @return + */ + def retryInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String): CompletableFuture[Void] = + toCompletableFutureVoid(baker.retryInteraction(recipeInstanceId, interactionName)) + + /** + * Resolves a blocked interaction by giving it's output. + * + * @param recipeInstanceId The process identifier. + * @param interactionName The name of the blocked interaction. + * @param event The output of the interaction. + * @return + */ + def resolveInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String, @Nonnull event: EventInstance): CompletableFuture[Void] = + toCompletableFutureVoid(baker.resolveInteraction(recipeInstanceId, interactionName, event.asScala)) + + /** + * Stops a retrying interaction. + * + * @param recipeInstanceId The process identifier. + * @param interactionName The name of the retrying interaction. + * @return + */ + def stopRetryingInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String): CompletableFuture[Void] = + toCompletableFutureVoid(baker.stopRetryingInteraction(recipeInstanceId, interactionName)) + + /** + * Check if recipe instance exists and is active. + * + * @param recipeInstanceId The recipe instance identifier + * @return true if recipe instance exists, and is in active state, otherwise false. + */ + def hasRecipeInstance(recipeInstanceId: String): CompletableFuture[Boolean] = + toCompletableFuture(baker.hasRecipeInstance(recipeInstanceId)) + + /** + * Returns the state of a process instance. This includes the ingredients and names of the events. + * + * @param recipeInstanceId The process identifier + * @return The state of the process instance + */ + def getRecipeInstanceState(@Nonnull recipeInstanceId: String): CompletableFuture[RecipeInstanceState] = + toCompletableFuture(baker.getRecipeInstanceState(recipeInstanceId)).thenApply(_.asJava) + + /** + * @param recipeInstanceId The recipeInstance Id. + * @param name The name of the ingredient. + * @return The provided ingredients. + */ + override def getIngredient(recipeInstanceId: String, name: String): CompletableFuture[Value] = + toCompletableFuture(baker.getIngredient(recipeInstanceId, name)) + + /** + * Returns all the ingredients that are accumulated for a given process. + * + * @param recipeInstanceId The process identifier + * @return + */ + def getIngredients(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.Map[String, Value]] = + toCompletableFutureMap(baker.getIngredients(recipeInstanceId)) + + /** + * Returns all fired events for a given RecipeInstance id. + * + * @param recipeInstanceId The process id. + * @return The events + */ + @nowarn + def getEvents(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.List[EventMoment]] = + toCompletableFuture(baker.getEvents(recipeInstanceId)).thenApply(_.map(_.asJava()).asJava) + + /** + * Returns all names of fired events for a given RecipeInstance id. + * + * @param recipeInstanceId The process id. + * @return The event names + */ + @nowarn + def getEventNames(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.List[String]] = + toCompletableFuture(baker.getEventNames(recipeInstanceId)).thenApply(_.asJava) + + /** + * Returns the recipe information for the given RecipeId + * + * @param recipeId the recipeId + * @return The JRecipeInformation recipe + */ + override def getRecipe(@Nonnull recipeId: String): CompletableFuture[RecipeInformation] = + toCompletableFuture(baker.getRecipe(recipeId)).thenApply(_.asJava) + + override def getRecipeVisual(recipeId: String, style: RecipeVisualStyle): CompletableFuture[String] = + toCompletableFuture(baker.getRecipeVisual(recipeId)) + + /** + * Return alls 'active' recipes added to this Baker + * + * @return A map with all recipes from recipeId -> JRecipeInformation + */ + @nowarn + def getAllRecipes: CompletableFuture[java.util.Map[String, RecipeInformation]] = + baker.getAllRecipes.asJava.toCompletableFuture.thenApply(_.view.map { case (key, value) => (key, value.asJava) }.toMap.asJava) + + @nowarn + def getInteraction(interactionName: String): CompletableFuture[Optional[InteractionInstanceDescriptorType]] = + baker.getInteraction(interactionName).asJava + .toCompletableFuture + .thenApply(e => Optional.ofNullable(e.map(_.asJava()).orNull)) + + @nowarn + def getAllInteractions: CompletableFuture[java.util.List[InteractionInstanceDescriptorType]] = + baker.getAllInteractions.asJava + .toCompletableFuture + .thenApply(_.map(_.asJava()).asJava) + + @nowarn + def executeSingleInteraction(interactionId: String, ingredients: util.List[IngredientInstanceType]): CompletableFuture[InteractionExecutionResult] = + baker.executeSingleInteraction(interactionId, ingredients.asScala.map(_.asScala).toIndexedSeq).asJava + .toCompletableFuture + .thenApply(_.asJava) + + /** + * Returns an index of all processes. + * + * Can potentially return a partial index when baker runs in cluster mode + * and not all shards can be reached within the given timeout. + * + * Does not include deleted processes. + * + * @return An index of all processes + */ + @nowarn + def getAllRecipeInstancesMetadata: CompletableFuture[util.Set[RecipeInstanceMetadata]] = + baker.getAllRecipeInstancesMetadata.asJava + .toCompletableFuture + .thenApply(_.map(_.asJava).asJava) + + /** + * Registers a listener to all runtime events for this baker instance. + * + * Note that: + * + * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). + * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. + * + * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: + * + * - logging + * - metrics + * - unit tests + * - ... + * + * @param recipeName the name of all recipes this event listener should be triggered for + * @param listenerFunction The listener to subscribe to events. + */ + override def registerEventListener(@Nonnull recipeName: String, @Nonnull listenerFunction: BiConsumer[RecipeEventMetadata, String]): CompletableFuture[Void] = + toCompletableFutureVoid(baker.registerEventListener(recipeName, + (recipeEventMetadata: scaladsl.RecipeEventMetadata, event: String) => listenerFunction.accept(recipeEventMetadata.asJava, event))) + + /** + * Registers a listener function to all runtime events for this baker instance. + * + * Note that: + * + * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). + * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. + * + * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: + * + * - logging + * - metrics + * - unit tests + * - ... + * + * @param listenerFunction The listener function that is called once these events occur + */ + override def registerEventListener(@Nonnull listenerFunction: BiConsumer[RecipeEventMetadata, String]): CompletableFuture[Void] = + toCompletableFutureVoid(baker.registerEventListener( + (recipeEventMetadata: scaladsl.RecipeEventMetadata, event: String) => listenerFunction.accept(recipeEventMetadata.asJava, event))) + + /** + * Registers a listener function to all runtime events for this baker instance. + * + * Note that: + * + * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). + * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. + * + * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: + * + * - logging + * - metrics + * - unit tests + * - ... + * + * @param eventListener The EventListener class the processEvent will be called once these events occur + */ + @Deprecated + @deprecated(message = "Replaced with the consumer function variant", since = "3.0.0") + def registerEventListener(@Nonnull eventListener: EventListener): CompletableFuture[Void] = + toCompletableFutureVoid( + baker.registerEventListener((recipeEventMetadata: scaladsl.RecipeEventMetadata, event: String) => + eventListener.processEvent(recipeEventMetadata.recipeInstanceId, event))) + + /** + * Registers a listener that listens to all Baker events + * + * @param listenerFunction Listener function that is called once these events occur + * @return + */ + override def registerBakerEventListener(@Nonnull listenerFunction: Consumer[BakerEvent]): CompletableFuture[Void] = + toCompletableFutureVoid(baker.registerBakerEventListener((event: scaladsl.BakerEvent) => listenerFunction.accept(event.asJava))) + + /** + * Returns the visual state of the recipe in dot format with a default timeout of 20 seconds + * + * @param recipeInstanceId The process identifier + * @return + */ + def getVisualState(@Nonnull recipeInstanceId: String, @Nonnull style: RecipeVisualStyle): CompletableFuture[String] = + toCompletableFuture(baker.getVisualState(recipeInstanceId, style)) + + @nowarn + private def toCompletableFuture[T](@Nonnull scalaFuture: Future[T]): CompletableFuture[T] = + scalaFuture.asJava.toCompletableFuture + + @nowarn + private def toCompletableFutureVoid(@Nonnull scalaFuture: Future[Unit]): CompletableFuture[Void] = + scalaFuture.asJava.toCompletableFuture.thenApply((_: Unit) => null.asInstanceOf[Void]) + + @nowarn + private def toCompletableFutureMap[K, V](@Nonnull scalaFuture: Future[Map[K, V]]): CompletableFuture[java.util.Map[K, V]] = + scalaFuture.asJava + .toCompletableFuture + .thenApply(_.asJava) + + /** + * This method is used to add metadata to your request. This will be added to the ingredients map in Baker. + * Since this is meant to be used as metadata this should not + * These cannot be ingredients already found in your recipe. + * + * @param metadata Metadata + */ + override def addMetaData(recipeInstanceId: String, metadata: java.util.Map[String, String]): CompletableFuture[Void] = { + val x: Map[String, String] = metadata.asScala.toMap + toCompletableFutureVoid(baker.addMetaData(recipeInstanceId, x)) + } + + override def fireSensoryEventAndAwaitReceived(recipeInstanceId: String, event: EventInstance, correlationId: Optional[String]): CompletableFuture[SensoryEventStatus] = + toCompletableFuture(baker.fireSensoryEventAndAwaitReceived(recipeInstanceId, event.asScala, Option.apply(correlationId.orElse(null)))) + + override def awaitEvent(recipeInstanceId: String, eventName: String, timeout: Duration, waitForNext: Boolean): CompletableFuture[Void] = + toCompletableFutureVoid( + baker.awaitEvent(recipeInstanceId, eventName, FiniteDuration.apply(timeout.toMillis, TimeUnit.MILLISECONDS), waitForNext) + ) + + override def awaitCompleted(recipeInstanceId: String, timeout: Duration): CompletableFuture[SensoryEventStatus] = + toCompletableFuture(baker.awaitCompleted(recipeInstanceId, FiniteDuration.apply(timeout.toMillis, TimeUnit.MILLISECONDS))) +} diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala new file mode 100644 index 000000000..100df4589 --- /dev/null +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala @@ -0,0 +1,392 @@ +package com.ing.baker.runtime.javadsl + +import com.ing.baker.il.{CompiledRecipe, RecipeVisualStyle} +import com.ing.baker.runtime.common.LanguageDataStructures.JavaApi +import com.ing.baker.runtime.common.{RecipeRecord, SensoryEventStatus} +import com.ing.baker.runtime.common +import com.ing.baker.types.Value + +import java.time.Duration +import java.util +import java.util.Optional +import java.util.concurrent.CompletableFuture +import java.util.function.{BiConsumer, Consumer} +import javax.annotation.Nonnull +import scala.annotation.nowarn + +trait BakerCommon extends common.Baker[CompletableFuture] with JavaApi with AutoCloseable { + + override type SensoryEventResultType = SensoryEventResult + + override type EventResolutionsType = EventResolutions + + override type EventInstanceType = EventInstance + + override type RecipeInstanceStateType = RecipeInstanceState + + override type InteractionInstanceType = InteractionInstance + + override type InteractionInstanceDescriptorType = InteractionInstanceDescriptor + + override type IngredientInstanceType = IngredientInstance + + override type BakerEventType = BakerEvent + + override type RecipeInstanceMetadataType = RecipeInstanceMetadata + + override type RecipeInformationType = RecipeInformation + + override type EventMomentType = EventMoment + + override type RecipeMetadataType = RecipeEventMetadata + + override type InteractionExecutionResultType = InteractionExecutionResult + + override type DurationType = Duration + + override type UnitType = Void + + /** + * Adds a recipe to baker and returns a recipeId for the recipe. + * + * This function is idempotent, if the same (equal) recipe was added earlier this will return the same recipeId. + * + * @param recipeRecord The RecipeRecord recipe. + * @return A recipe identifier. + */ + def addRecipe(@Nonnull recipeRecord: RecipeRecord): CompletableFuture[String] + + /** + * Adds a recipe to baker and returns a recipeId for the recipe. + * + * This function is idempotent, if the same (equal) recipe was added earlier this will return the same recipeId + * + * @param compiledRecipe The compiled recipe. + * @return A recipeId + */ + override def addRecipe(compiledRecipe: CompiledRecipe, timeCreated: Long, validate: Boolean): CompletableFuture[String] = + addRecipe(RecipeRecord.of(compiledRecipe, updated = timeCreated, validate = validate)) + + /** + * Adds a recipe to baker and returns a recipeId for the recipe. + * + * This function is idempotent, if the same (equal) recipe was added earlier this will return the same recipeId + * + * @param compiledRecipe The compiled recipe. + * @return A recipeId + */ + override def addRecipe(compiledRecipe: CompiledRecipe, validate: Boolean): CompletableFuture[String] = + addRecipe(compiledRecipe, System.currentTimeMillis(), validate) + + /** + * Attempts to gracefully shutdown the baker system. + */ + def gracefulShutdown(): CompletableFuture[UnitType] + + /** + * This bakes (creates) a new process instance of the recipe. + * + * @param recipeId The recipe this instance will be baked for + * @param recipeInstanceId The process identifier + */ + def bake(@Nonnull recipeId: String, @Nonnull recipeInstanceId: String): CompletableFuture[UnitType] + + /** + * Creates a process instance for the given recipeId with the given RecipeInstanceId as identifier + * This variant also gets a metadata map added on bake. + * This is similar to calling addMetaData after doing the regular bake but depending on the implementation this can be more optimized. + * + * @param recipeId The recipeId for the recipe to bake + * @param recipeInstanceId The identifier for the newly baked process + * @param metadata Metadata + * @return + */ + def bake(recipeId: String, recipeInstanceId: String, metadata: util.Map[String, String]): CompletableFuture[UnitType] + + def deleteRecipeInstance(recipeInstanceId: String, removeFromIndex: Boolean): CompletableFuture[UnitType] + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") + def fireEventAndResolveWhenReceived(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: String): CompletableFuture[SensoryEventStatus] = + fireEventAndResolveWhenReceived(recipeInstanceId, event, Optional.of(correlationId)) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitCompleted.", "5.1.0") + def fireEventAndResolveWhenCompleted(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: String): CompletableFuture[SensoryEventResult] = + fireEventAndResolveWhenCompleted(recipeInstanceId, event, Optional.of(correlationId)) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitEvent.", "5.1.0") + def fireEventAndResolveOnEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull onEvent: String, @Nonnull correlationId: String): CompletableFuture[SensoryEventResult] = + fireEventAndResolveOnEvent(recipeInstanceId, event, onEvent, Optional.of(correlationId)) + + @Deprecated + @deprecated("This method uses a callback-style API that is deprecated and will be removed after December 1st, 2026. Please use the new composable API: fireSensoryEventAndAwaitReceived followed by awaitCompleted or awaitEvent.", "5.1.0") + def fireEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: String): EventResolutions = + fireEvent(recipeInstanceId, event, Optional.of(correlationId)) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") + def fireEventAndResolveWhenReceived(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance): CompletableFuture[SensoryEventStatus] = + fireEventAndResolveWhenReceived(recipeInstanceId, event, Optional.empty[String]()) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitCompleted.", "5.1.0") + def fireEventAndResolveWhenCompleted(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance): CompletableFuture[SensoryEventResult] = + fireEventAndResolveWhenCompleted(recipeInstanceId, event, Optional.empty[String]()) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitEvent.", "5.1.0") + def fireEventAndResolveOnEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull onEvent: String): CompletableFuture[SensoryEventResult] = + fireEventAndResolveOnEvent(recipeInstanceId, event, onEvent, Optional.empty[String]()) + + @Deprecated + @deprecated("This method uses a callback-style API that is deprecated and will be removed after December 1st, 2026. Please use the new composable API: fireSensoryEventAndAwaitReceived followed by awaitCompleted or awaitEvent.", "5.1.0") + def fireEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance): EventResolutions = + fireEvent(recipeInstanceId, event, Optional.empty[String]()) + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") + def fireEventAndResolveWhenReceived(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventStatus] + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitCompleted.", "5.1.0") + @nowarn + def fireEventAndResolveWhenCompleted(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventResult] + + @Deprecated + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use the combination of fireSensoryEventAndAwaitReceived followed by awaitEvent.", "5.1.0") + @nowarn + def fireEventAndResolveOnEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull onEvent: String, @Nonnull correlationId: Optional[String]): CompletableFuture[SensoryEventResult] + + @Deprecated + @deprecated("This method uses a callback-style API that is deprecated and will be removed after December 1st, 2026. Please use the new composable API: fireSensoryEventAndAwaitReceived followed by awaitCompleted or awaitEvent.", "5.1.0") + @nowarn + def fireEvent(@Nonnull recipeInstanceId: String, @Nonnull event: EventInstance, @Nonnull correlationId: Optional[String]): EventResolutions + + /** + * Retries a blocked interaction. + * + * @param recipeInstanceId The process identifier. + * @param interactionName The name of the blocked interaction. + * @return + */ + def retryInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String): CompletableFuture[UnitType] + + /** + * Resolves a blocked interaction by giving it's output. + * + * @param recipeInstanceId The process identifier. + * @param interactionName The name of the blocked interaction. + * @param event The output of the interaction. + * @return + */ + def resolveInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String, @Nonnull event: EventInstance): CompletableFuture[UnitType] + + /** + * Stops a retrying interaction. + * + * @param recipeInstanceId The process identifier. + * @param interactionName The name of the retrying interaction. + * @return + */ + def stopRetryingInteraction(@Nonnull recipeInstanceId: String, @Nonnull interactionName: String): CompletableFuture[UnitType] + + /** + * Check if recipe instance exists and is active. + * + * @param recipeInstanceId The recipe instance identifier + * @return true if recipe instance exists, and is in active state, otherwise false. + */ + def hasRecipeInstance(recipeInstanceId: String): CompletableFuture[Boolean] + + /** + * Returns the state of a process instance. This includes the ingredients and names of the events. + * + * @param recipeInstanceId The process identifier + * @return The state of the process instance + */ + def getRecipeInstanceState(@Nonnull recipeInstanceId: String): CompletableFuture[RecipeInstanceState] + + /** + * @param recipeInstanceId The recipeInstance Id. + * @param name The name of the ingredient. + * @return The provided ingredients. + */ + def getIngredient(recipeInstanceId: String, name: String): CompletableFuture[Value] + + /** + * Returns all the ingredients that are accumulated for a given process. + * + * @param recipeInstanceId The process identifier + * @return + */ + def getIngredients(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.Map[String, Value]] + + /** + * Returns all fired events for a given RecipeInstance id. + * + * @param recipeInstanceId The process id. + * @return The events + */ + @nowarn + def getEvents(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.List[EventMoment]] + + /** + * Returns all names of fired events for a given RecipeInstance id. + * + * @param recipeInstanceId The process id. + * @return The event names + */ + @nowarn + def getEventNames(@Nonnull recipeInstanceId: String): CompletableFuture[java.util.List[String]] + + /** + * Returns the recipe information for the given RecipeId + * + * @param recipeId the recipeId + * @return The JRecipeInformation recipe + */ + def getRecipe(@Nonnull recipeId: String): CompletableFuture[RecipeInformation] + + def getRecipeVisual(recipeId: String, style: RecipeVisualStyle): CompletableFuture[String] + + /** + * Return alls 'active' recipes added to this Baker + * + * @return A map with all recipes from recipeId -> JRecipeInformation + */ + @nowarn + def getAllRecipes: CompletableFuture[java.util.Map[String, RecipeInformation]] + + @nowarn + def getInteraction(interactionName: String): CompletableFuture[Optional[InteractionInstanceDescriptorType]] + + @nowarn + def getAllInteractions: CompletableFuture[java.util.List[InteractionInstanceDescriptorType]] + + @nowarn + def executeSingleInteraction(interactionId: String, ingredients: util.List[IngredientInstanceType]): CompletableFuture[InteractionExecutionResult] + + /** + * Returns an index of all processes. + * + * Can potentially return a partial index when baker runs in cluster mode + * and not all shards can be reached within the given timeout. + * + * Does not include deleted processes. + * + * @return An index of all processes + */ + @nowarn + def getAllRecipeInstancesMetadata: CompletableFuture[util.Set[RecipeInstanceMetadata]] + + /** + * Registers a listener to all runtime events for this baker instance. + * + * Note that: + * + * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). + * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. + * + * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: + * + * - logging + * - metrics + * - unit tests + * - ... + * + * @param recipeName the name of all recipes this event listener should be triggered for + * @param listenerFunction The listener to subscribe to events. + */ + override def registerEventListener(@Nonnull recipeName: String, @Nonnull listenerFunction: BiConsumer[RecipeEventMetadata, String]): CompletableFuture[UnitType] + + /** + * Registers a listener function to all runtime events for this baker instance. + * + * Note that: + * + * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). + * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. + * + * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: + * + * - logging + * - metrics + * - unit tests + * - ... + * + * @param listenerFunction The listener function that is called once these events occur + */ + override def registerEventListener(@Nonnull listenerFunction: BiConsumer[RecipeEventMetadata, String]): CompletableFuture[UnitType] + + /** + * Registers a listener function to all runtime events for this baker instance. + * + * Note that: + * + * - The delivery guarantee is *AT MOST ONCE*. Practically this means you can miss events when the application terminates (unexpected or not). + * - The delivery is local (JVM) only, you will NOT receive events from other nodes when running in cluster mode. + * + * Because of these constraints you should not use an event listener for critical functionality. Valid use cases might be: + * + * - logging + * - metrics + * - unit tests + * - ... + * + * @param eventListener The EventListener class the processEvent will be called once these events occur + */ + @Deprecated + @deprecated(message = "Replaced with the consumer function variant", since = "3.0.0") + def registerEventListener(@Nonnull eventListener: EventListener): CompletableFuture[UnitType] + + /** + * Registers a listener that listens to all Baker events + * + * @param listenerFunction + * @return + */ + override def registerBakerEventListener(@Nonnull listenerFunction: Consumer[BakerEvent]): CompletableFuture[UnitType] + + /** + * Returns the visual state of the recipe in dot format with a default timeout of 20 seconds + * + * @param recipeInstanceId The process identifier + * @return + */ + def getVisualState(@Nonnull recipeInstanceId: String): CompletableFuture[String] = + getVisualState(recipeInstanceId, RecipeVisualStyle.default) + + /** + * Returns the visual state of the recipe in dot format with a default timeout of 20 seconds + * + * @param recipeInstanceId The process identifier + * @return + */ + def getVisualState(@Nonnull recipeInstanceId: String, @Nonnull style: RecipeVisualStyle): CompletableFuture[String] + + /** + * This method is used to add metadata to your request. This will be added to the ingredients map in Baker. + * Since this is meant to be used as metadata this should not + * These cannot be ingredients already found in your recipe. + * + * @param metadata + */ + override def addMetaData(recipeInstanceId: String, metadata: java.util.Map[String, String]): CompletableFuture[UnitType] + + def fireSensoryEventAndAwaitReceived(recipeInstanceId: String, event: EventInstance): CompletableFuture[SensoryEventStatus] = + fireSensoryEventAndAwaitReceived(recipeInstanceId, event, Optional.empty()) + + override def fireSensoryEventAndAwaitReceived(recipeInstanceId: String, event: EventInstance, correlationId: Optional[String]): CompletableFuture[SensoryEventStatus] + + /** + * Convenience overload for Java callers that don't want to specify waitForNext. + */ + def awaitEvent(recipeInstanceId: String, eventName: String, timeout: Duration): CompletableFuture[UnitType] = + awaitEvent(recipeInstanceId, eventName, timeout, waitForNext = false) + + override def awaitEvent(recipeInstanceId: String, eventName: String, timeout: Duration, waitForNext: Boolean): CompletableFuture[UnitType] + + override def awaitCompleted(recipeInstanceId: String, timeout: Duration): CompletableFuture[SensoryEventStatus] +} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerConfig.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala similarity index 58% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerConfig.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala index 38fa53715..467600ae8 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerConfig.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala @@ -1,12 +1,11 @@ -package com.ing.baker.runtime.javadsl +package com.ing.baker.runtime.model -import com.ing.baker.runtime.model.BakerF +import com.ing.baker.runtime.model.recipeinstance.RecipeInstanceConfig import java.time.Duration -import scala.concurrent.duration.{FiniteDuration, NANOSECONDS} object BakerConfig { - def defaults(): BakerConfig = { + def default(): BakerConfig = { new BakerConfig( false, RecipeInstanceConfig(), @@ -23,16 +22,16 @@ object BakerConfig { } case class BakerConfig( - val allowAddingRecipeWithoutRequiringInstances: Boolean, - val recipeInstanceConfig: RecipeInstanceConfig, - val idleTimeout: Duration, - val retentionPeriodCheckInterval: Duration, - val bakeTimeout: Duration, - val processEventTimeout: Duration, - val inquireTimeout: Duration, - val shutdownTimeout: Duration, - val addRecipeTimeout: Duration, - val executeSingleInteractionTimeout: Duration) { + allowAddingRecipeWithoutRequiringInstances: Boolean, + recipeInstanceConfig: RecipeInstanceConfig, + idleTimeout: Duration, + retentionPeriodCheckInterval: Duration, + bakeTimeout: Duration, + processEventTimeout: Duration, + inquireTimeout: Duration, + shutdownTimeout: Duration, + addRecipeTimeout: Duration, + executeSingleInteractionTimeout: Duration) { def withAllowAddingRecipeWithoutRequiringInstances(allowAddingRecipeWithoutRequiringInstances: Boolean): BakerConfig = copy(allowAddingRecipeWithoutRequiringInstances = allowAddingRecipeWithoutRequiringInstances) @@ -62,21 +61,4 @@ case class BakerConfig( def withExecuteSingleInteractionTimeout(executeSingleInteractionTimeout: Duration): BakerConfig = copy(executeSingleInteractionTimeout = executeSingleInteractionTimeout) - - def toBakerFConfig(): BakerF.Config = { - implicit def toScalaDuration(duration: Duration): FiniteDuration = FiniteDuration.apply(duration.toNanos, NANOSECONDS) - - BakerF.Config( - allowAddingRecipeWithoutRequiringInstances, - recipeInstanceConfig.toBakerFRecipeInstanceConfig(), - idleTimeout, - retentionPeriodCheckInterval, - bakeTimeout, - processEventTimeout, - inquireTimeout, - shutdownTimeout, - addRecipeTimeout, - executeSingleInteractionTimeout - ) - } } diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerF.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerF.scala similarity index 93% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerF.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerF.scala index 90c500462..e6dcd69a4 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerF.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerF.scala @@ -11,36 +11,21 @@ import com.ing.baker.runtime.common import com.ing.baker.runtime.common.BakerException.NoSuchIngredientException import com.ing.baker.runtime.common.LanguageDataStructures.ScalaApi import com.ing.baker.runtime.common.{BakerException, InteractionExecutionFailureReason, RecipeRecord, SensoryEventStatus} -import com.ing.baker.runtime.model.recipeinstance.RecipeInstance import com.ing.baker.runtime.scaladsl.{Baker => DeprecatedBaker, _} import com.ing.baker.types.Value import com.typesafe.scalalogging.LazyLogging import scala.concurrent.Future import scala.concurrent.duration._ +import scala.jdk.DurationConverters._ /** * TODO create a Resource based runtime execution which runs the retention period stream and allocates a Blocker context */ -object BakerF { - - case class Config(allowAddingRecipeWithoutRequiringInstances: Boolean = false, - recipeInstanceConfig: RecipeInstance.Config = RecipeInstance.Config(), - idleTimeout: FiniteDuration = 60 seconds, - retentionPeriodCheckInterval: FiniteDuration = 10.seconds, - bakeTimeout: FiniteDuration = 10.seconds, - processEventTimeout: FiniteDuration = 10.seconds, - inquireTimeout: FiniteDuration = 60.seconds, - shutdownTimeout: FiniteDuration = 10.seconds, - addRecipeTimeout: FiniteDuration = 10.seconds, - executeSingleInteractionTimeout: FiniteDuration = 60.seconds, - ) -} - abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[F], async: Async[F]) extends common.Baker[F] with ScalaApi with LazyLogging { self => - val config: BakerF.Config + val config: BakerConfig override type SensoryEventResultType = SensoryEventResult @@ -70,6 +55,8 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ override type DurationType = FiniteDuration + override type UnitType = Unit + private def javaTimeoutToBakerTimeout[A](operationName: String) : PartialFunction[Throwable, F[A]] = { case _ : java.util.concurrent.TimeoutException => sync.raiseError(BakerException.TimeoutException(operationName)) } @@ -86,7 +73,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ components .recipeManager .addRecipe(recipeRecord.recipe, !recipeRecord.validate || config.allowAddingRecipeWithoutRequiringInstances) - .timeout(config.addRecipeTimeout) + .timeout(config.addRecipeTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("addRecipe")) /** @@ -97,7 +84,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def getRecipe(recipeId: String): F[RecipeInformation] = components.recipeManager.getRecipe(recipeId) - .timeout(config.inquireTimeout) + .timeout(config.inquireTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("getRecipe")) @@ -112,7 +99,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def getAllRecipes: F[Map[String, RecipeInformation]] = components.recipeManager.getAllRecipes - .timeout(config.inquireTimeout) + .timeout(config.inquireTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("getAllRecipes")) @@ -143,7 +130,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ Some(interactionInstance.name), Some(s"Interaction execution failed. Interaction threw ${e.getClass.getSimpleName} with message ${e.getMessage}.")))) } - .timeoutTo(config.executeSingleInteractionTimeout, sync.pure(InteractionExecutionResult(Left(InteractionExecutionResult.Failure( + .timeoutTo(config.executeSingleInteractionTimeout.toScala, sync.pure(InteractionExecutionResult(Left(InteractionExecutionResult.Failure( InteractionExecutionFailureReason.TIMEOUT, Some(interactionInstance.name), None))))) .recoverWith(javaTimeoutToBakerTimeout("executeSingleInteraction")) } @@ -163,7 +150,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def bake(recipeId: String, recipeInstanceId: String): F[Unit] = components.recipeInstanceManager.bake(recipeId, recipeInstanceId, config.recipeInstanceConfig) - .timeout(config.bakeTimeout) + .timeout(config.bakeTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("bake")) /** @@ -175,7 +162,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def bake(recipeId: String, recipeInstanceId: String, metadata: Map[String, String]): F[Unit] = components.recipeInstanceManager.bake(recipeId, recipeInstanceId, config.recipeInstanceConfig) - .timeout(config.bakeTimeout) + .timeout(config.bakeTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("bake")) .flatMap(_ => components.recipeInstanceManager.addMetaData(recipeInstanceId, metadata)) @@ -203,7 +190,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ override def fireEventAndResolveWhenReceived(recipeInstanceId: String, event: EventInstance, correlationId: Option[String]): F[SensoryEventStatus] = components.recipeInstanceManager .fireEventAndResolveWhenReceived(recipeInstanceId, event, correlationId) - .timeout(config.processEventTimeout) + .timeout(config.processEventTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("fireEventAndResolveWhenReceived")) /** @@ -220,7 +207,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ override def fireEventAndResolveWhenCompleted(recipeInstanceId: String, event: EventInstance, correlationId: Option[String]): F[SensoryEventResult] = components.recipeInstanceManager .fireEventAndResolveWhenCompleted(recipeInstanceId, event, correlationId) - .timeout(config.processEventTimeout) + .timeout(config.processEventTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("fireEventAndResolveWhenCompleted")) /** @@ -238,7 +225,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ override def fireEventAndResolveOnEvent(recipeInstanceId: String, event: EventInstance, onEvent: String, correlationId: Option[String]): F[SensoryEventResult] = components.recipeInstanceManager .fireEventAndResolveOnEvent(recipeInstanceId, event, onEvent, correlationId) - .timeout(config.processEventTimeout) + .timeout(config.processEventTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("fireEventAndResolveOnEvent")) /** @@ -264,16 +251,16 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ new EventResolutionsF[F] { override def resolveWhenReceived: F[SensoryEventStatus] = - ioCAST._1.timeout(config.processEventTimeout).recoverWith(javaTimeoutToBakerTimeout("fireEvent")) + ioCAST._1.timeout(config.processEventTimeout.toScala).recoverWith(javaTimeoutToBakerTimeout("fireEvent")) override def resolveWhenCompleted: F[SensoryEventResult] = - ioCAST._2.timeout(config.processEventTimeout).recoverWith(javaTimeoutToBakerTimeout("fireEvent")) + ioCAST._2.timeout(config.processEventTimeout.toScala).recoverWith(javaTimeoutToBakerTimeout("fireEvent")) } } else { new EventResolutionsF[F] { override def resolveWhenReceived: F[SensoryEventStatus] = - result._1F.timeout(config.processEventTimeout).recoverWith(javaTimeoutToBakerTimeout("fireEvent")).flatten + result._1F.timeout(config.processEventTimeout.toScala).recoverWith(javaTimeoutToBakerTimeout("fireEvent")).flatten override def resolveWhenCompleted: F[SensoryEventResult] = - result._2F.timeout(config.processEventTimeout).recoverWith(javaTimeoutToBakerTimeout("fireEvent")).flatten + result._2F.timeout(config.processEventTimeout.toScala).recoverWith(javaTimeoutToBakerTimeout("fireEvent")).flatten } } } @@ -308,7 +295,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ override def fireSensoryEventAndAwaitReceived(recipeInstanceId: String, event: EventInstance, correlationId: Option[String]): F[SensoryEventStatus] = components.recipeInstanceManager .fireSensoryEventAndAwaitReceived(recipeInstanceId, event, correlationId) - .timeout(config.processEventTimeout) + .timeout(config.processEventTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("fireSensoryEventAndAwaitReceived")) override def awaitCompleted(recipeInstanceId: String, timeout: FiniteDuration): F[SensoryEventStatus] = @@ -335,7 +322,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def getAllRecipeInstancesMetadata: F[Set[RecipeInstanceMetadata]] = components.recipeInstanceManager.getAllRecipeInstancesMetadata - .timeout(config.inquireTimeout) + .timeout(config.inquireTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("getAllRecipeInstancesMetadata")) /** @@ -346,7 +333,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def hasRecipeInstance(recipeInstanceId: String): F[Boolean] = components.recipeInstanceManager.hasRecipeInstance(recipeInstanceId) - .timeout(config.inquireTimeout) + .timeout(config.inquireTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("hasRecipeInstance")) /** @@ -357,7 +344,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def getRecipeInstanceState(recipeInstanceId: String): F[RecipeInstanceState] = components.recipeInstanceManager.getRecipeInstanceState(recipeInstanceId) - .timeout(config.inquireTimeout) + .timeout(config.inquireTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("getRecipeInstanceState")) /** @@ -409,7 +396,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def getVisualState(recipeInstanceId: String, style: RecipeVisualStyle = RecipeVisualStyle.default): F[String] = components.recipeInstanceManager.getVisualState(recipeInstanceId, style) - .timeout(config.inquireTimeout) + .timeout(config.inquireTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("getVisualState")) private def doRegisterEventListener(listenerFunction: (RecipeEventMetadata, String) => Unit, processFilter: String => Boolean): F[Unit] = @@ -451,7 +438,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def registerBakerEventListener(listenerFunction: BakerEvent => Unit): F[Unit] = components.eventStream.subscribe(listenerFunction) - .timeout(config.inquireTimeout) + .timeout(config.inquireTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("registerBakerEventListener")) /** @@ -462,7 +449,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ override def retryInteraction(recipeInstanceId: String, interactionName: String): F[Unit] = components.recipeInstanceManager.retryBlockedInteraction(recipeInstanceId, interactionName) .flatMap(_.compile.drain) - .timeout(config.processEventTimeout) + .timeout(config.processEventTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("retryInteraction")) /** @@ -475,7 +462,7 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ override def resolveInteraction(recipeInstanceId: String, interactionName: String, event: EventInstance): F[Unit] = components.recipeInstanceManager.resolveBlockedInteraction(recipeInstanceId, interactionName, event) .flatMap(_.compile.drain) - .timeout(config.processEventTimeout) + .timeout(config.processEventTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("resolveInteraction")) /** @@ -485,12 +472,12 @@ abstract class BakerF[F[_]](implicit components: BakerComponents[F], sync: Sync[ */ override def stopRetryingInteraction(recipeInstanceId: String, interactionName: String): F[Unit] = components.recipeInstanceManager.stopRetryingInteraction(recipeInstanceId, interactionName) - .timeout(config.processEventTimeout) + .timeout(config.processEventTimeout.toScala) .recoverWith(javaTimeoutToBakerTimeout("stopRetryingInteraction")) def translate[G[_]](mapK: F ~> G, comapK: G ~> F)(implicit components: BakerComponents[G], async: Async[G]): BakerF[G] = new BakerF[G] { - override val config: BakerF.Config = + override val config: BakerConfig = self.config override def addRecipe(recipeRecord: RecipeRecord): G[String] = mapK(self.addRecipe(recipeRecord)) diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/EventStream.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/EventStream.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/EventStream.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/EventStream.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala similarity index 95% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala index 70ef6caff..47af49857 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala @@ -10,9 +10,51 @@ import com.ing.baker.runtime.model.recipeinstance.RecipeInstance.{FatalInteracti import com.ing.baker.runtime.scaladsl.{EventInstance, IngredientInstance, InteractionInstanceInput} import com.ing.baker.types.Type import com.typesafe.config.ConfigFactory +import InteractionManager._ -import scala.collection.immutable.Seq +object InteractionManager { + private lazy val AllowSupersetForOutputTypes: Boolean = + ConfigFactory.load().getBoolean("baker.interactions.allow-superset-for-output-types") + + sealed trait InteractionIncompatible + case object NameNotFound extends InteractionIncompatible + case class InteractionMatchInputSizeFailed( + interactionName: String, + transitionArgsSize: Int, + implementationArgsSize: Int + ) extends InteractionIncompatible { + override def toString: String = + s"$interactionName input size differs: transition expects $transitionArgsSize, implementation provides $implementationArgsSize" + } + + case class InteractionMatchInputFailed( + interactionName: String, + transitionInputTypesMissing: Seq[IngredientDescriptor], + implementationInputTypesExtra: Seq[InteractionInstanceInput] + ) extends InteractionIncompatible { + override def toString: String = + s"$interactionName input types mismatch: transition expects $transitionInputTypesMissing, not provided by implementation, implementation provides extra: $implementationInputTypesExtra" + } + + case class InteractionMatchOutputSizeFailed(interactionName: String, + transitionArgsSize: Int, + implementationArgsSize: Int) extends InteractionIncompatible { + override def toString: String = + s"$interactionName output size differs: transition expects $transitionArgsSize, implementation provides $implementationArgsSize" + } + + case class InteractionMatchOutputNotFound(interactionName: String, + eventDescriptors: Seq[EventDescriptor]) extends InteractionIncompatible { + override def toString: String = + s"$interactionName ouput mismatch: transition expects $eventDescriptors, not provided by implementation" + } + + case class UnknownReason(interactionName: String) extends InteractionIncompatible { + override def toString: String = + s"$interactionName: unknown reason for no interaction matched" + } +} /** * The InteractionManager is responsible for keeping and calling al InteractionInstances. * The other components of Baker will not call InteractionInstances and will always go through the InteractionManager. @@ -28,8 +70,6 @@ trait InteractionManager[F[_]] { * If this new value is given from the implementation this will result in te runtime error and a technical failure of the interaction. */ def allowSupersetForOutputTypes: Boolean = AllowSupersetForOutputTypes - private lazy val AllowSupersetForOutputTypes: Boolean = - ConfigFactory.load().getBoolean("baker.interactions.allow-superset-for-output-types") def listAll: F[List[InteractionInstance[F]]] @@ -102,44 +142,6 @@ trait InteractionManager[F[_]] { )) } - sealed trait InteractionIncompatible - case object NameNotFound extends InteractionIncompatible - case class InteractionMatchInputSizeFailed( - interactionName: String, - transitionArgsSize: Int, - implementationArgsSize: Int - ) extends InteractionIncompatible { - override def toString: String = - s"$interactionName input size differs: transition expects $transitionArgsSize, implementation provides $implementationArgsSize" - } - - case class InteractionMatchInputFailed( - interactionName: String, - transitionInputTypesMissing: Seq[IngredientDescriptor], - implementationInputTypesExtra: Seq[InteractionInstanceInput] - ) extends InteractionIncompatible { - override def toString: String = - s"$interactionName input types mismatch: transition expects $transitionInputTypesMissing, not provided by implementation, implementation provides extra: $implementationInputTypesExtra" - } - - case class InteractionMatchOutputSizeFailed(interactionName: String, - transitionArgsSize: Int, - implementationArgsSize: Int) extends InteractionIncompatible { - override def toString: String = - s"$interactionName output size differs: transition expects $transitionArgsSize, implementation provides $implementationArgsSize" - } - - case class InteractionMatchOutputNotFound(interactionName: String, - eventDescriptors: Seq[EventDescriptor]) extends InteractionIncompatible { - override def toString: String = - s"$interactionName ouput mismatch: transition expects $eventDescriptors, not provided by implementation" - } - - case class UnknownReason(interactionName: String) extends InteractionIncompatible { - override def toString: String = - s"$interactionName: unknown reason for no interaction matched" - } - def incompatibilities(transition: InteractionTransition)(implicit sync: Sync[F]): F[Seq[InteractionIncompatible]] = for { all <- listAll } yield { diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala similarity index 95% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala index e416a7d7b..8c9069ff8 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala @@ -1,6 +1,5 @@ package com.ing.baker.runtime.model -import cats.Foldable.ops.toAllFoldableOps import cats.data.EitherT import cats.effect.implicits.{genSpawnOps, genTemporalOps_} import cats.effect.Async @@ -10,24 +9,20 @@ import com.ing.baker.il.{RecipeVisualStyle, RecipeVisualizer} import com.ing.baker.runtime.common.BakerException.{ProcessAlreadyExistsException, ProcessDeletedException} import com.ing.baker.runtime.common.RecipeInstanceState.RecipeInstanceMetadataName import com.ing.baker.runtime.common.{BakerException, SensoryEventStatus} -import com.ing.baker.runtime.model.RecipeInstanceManager.RecipeInstanceStatus -import com.ing.baker.runtime.model.recipeinstance.RecipeInstance +import com.ing.baker.runtime.model.recipeinstance.{RecipeInstance, RecipeInstanceConfig} import com.ing.baker.runtime.scaladsl.{EventInstance, RecipeInstanceMetadata, RecipeInstanceState, SensoryEventResult} import fs2.{Pipe, Stream} import scala.concurrent.duration.FiniteDuration import java.util.concurrent.TimeoutException -object RecipeInstanceManager { +sealed trait RecipeInstanceStatus[F[_]] - sealed trait RecipeInstanceStatus[F[_]] +object RecipeInstanceStatus { - object RecipeInstanceStatus { + case class Active[F[_]](recipeInstance: RecipeInstance[F], lastModified: Long) extends RecipeInstanceStatus[F] - case class Active[F[_]](recipeInstance: RecipeInstance[F], lastModified: Long) extends RecipeInstanceStatus[F] - - case class Deleted[F[_]](recipeId: String, createdOn: Long, deletedOn: Long) extends RecipeInstanceStatus[F] - } + case class Deleted[F[_]](recipeId: String, createdOn: Long, deletedOn: Long) extends RecipeInstanceStatus[F] } trait RecipeInstanceManager[F[_]] { @@ -53,7 +48,7 @@ trait RecipeInstanceManager[F[_]] { } } yield () - def bake(recipeId: String, recipeInstanceId: String, config: RecipeInstance.Config)(implicit components: BakerComponents[F], async: Async[F]): F[Unit] = + def bake(recipeId: String, recipeInstanceId: String, config: RecipeInstanceConfig)(implicit components: BakerComponents[F], async: Async[F]): F[Unit] = for { _ <- fetch(recipeInstanceId).flatMap[Unit] { case Some(RecipeInstanceStatus.Active(_, _)) => diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala similarity index 98% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala index 2fa7012a7..b9f2af199 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala @@ -1,6 +1,6 @@ package com.ing.baker.runtime.model -import cats.effect.{Async, Clock, Sync} +import cats.effect.{Async, Sync} import cats.implicits._ import com.ing.baker.il.CompiledRecipe import com.ing.baker.runtime.common.BakerException.{ImplementationsException, NoSuchRecipeException, RecipeValidationException} diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala similarity index 93% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala index 654d9b7c2..e45f7f238 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala @@ -2,8 +2,7 @@ package com.ing.baker.runtime.model.recipeinstance import cats.data.EitherT import cats.effect.kernel.Ref -import cats.effect.std.Dispatcher -import cats.effect.{Async, Deferred, IO, Sync} +import cats.effect.{Async, Deferred, Sync} import cats.implicits._ import com.ing.baker.il.CompiledRecipe import com.ing.baker.il.failurestrategy.ExceptionStrategyOutcome @@ -13,14 +12,13 @@ import com.ing.baker.runtime.scaladsl.{EventInstance, EventReceived, EventReject import com.typesafe.scalalogging.LazyLogging import fs2.Stream +import scala.jdk.OptionConverters.RichOptional +import scala.jdk.CollectionConverters.CollectionHasAsScala +import scala.jdk.DurationConverters._ import scala.concurrent.duration._ object RecipeInstance { - - case class Config(idleTTL: Option[FiniteDuration] = Some(5.seconds), - ingredientsFilter: Seq[String] = Seq.empty) - - def empty[F[_]](recipe: CompiledRecipe, recipeInstanceId: String, settings: Config)(implicit components: BakerComponents[F], async: Async[F]): F[RecipeInstance[F]] = + def empty[F[_]](recipe: CompiledRecipe, recipeInstanceId: String, settings: RecipeInstanceConfig)(implicit components: BakerComponents[F], async: Async[F]): F[RecipeInstance[F]] = for { timestamp <- async.pure(System.currentTimeMillis()) state <- Ref.of[F, RecipeInstanceState[F]](RecipeInstanceState.empty(recipeInstanceId, recipe, timestamp)) @@ -32,7 +30,7 @@ object RecipeInstance { class FatalInteractionException(message: String, cause: Throwable = null) extends RuntimeException(message, cause) } -case class RecipeInstance[F[_]](recipeInstanceId: String, config: RecipeInstance.Config, state: Ref[F, RecipeInstanceState[F]]) extends LazyLogging { +case class RecipeInstance[F[_]](recipeInstanceId: String, config: RecipeInstanceConfig, state: Ref[F, RecipeInstanceState[F]]) extends LazyLogging { private def updateStateAndNotify[A](update: RecipeInstanceState[F] => (RecipeInstanceState[F], A))(implicit async: Async[F]): F[A] = for { @@ -71,7 +69,7 @@ case class RecipeInstance[F[_]](recipeInstanceId: String, config: RecipeInstance } _ <- EitherT.liftF(components.eventStream.publish(EventReceived(currentTime, currentState.recipe.name, currentState.recipe.recipeId, recipeInstanceId, correlationId, input.name))) } yield baseCase(initialExecution) - .collect { case Some(output) => output.filterNot(config.ingredientsFilter) } + .collect { case Some(output) => output.filterNot(config.ingredientsFilter.asScala.toSeq) } def stopRetryingInteraction(interactionName: String)(implicit components: BakerComponents[F], async: Async[F]): F[Unit] = for { @@ -174,9 +172,9 @@ case class RecipeInstance[F[_]](recipeInstanceId: String, config: RecipeInstance private def scheduleIdleStop(implicit components: BakerComponents[F], async: Async[F]): F[Unit] = { def schedule: F[Unit] = state.get.flatMap { currentState => - config.idleTTL match { + config.idleTTL.toScala match { case Some(idleTTL) if currentState.isInactive => - async.sleep(idleTTL) *> confirmIdleStop(currentState.sequenceNumber, idleTTL) + async.sleep(idleTTL.toScala) *> confirmIdleStop(currentState.sequenceNumber, idleTTL.toScala) case _ => async.unit } } @@ -188,10 +186,10 @@ case class RecipeInstance[F[_]](recipeInstanceId: String, config: RecipeInstance async.delay(components.logging.idleStop(recipeInstanceId, originalIdleTTL)) else async.unit } - val scheduled: IO[Unit] = Dispatcher.parallel[IO](await = true).map { dispatcher: Dispatcher[IO] => - dispatcher.unsafeRunAndForget(IO.delay(schedule)) - }.use(_ => IO.unit) - Async[F].delay(scheduled) + + // Start the schedule computation in the background as a fiber and discard the result + // This allows the idle stop to happen asynchronously without blocking + async.start(schedule).void } private def getInteractionTransitionExecution(interactionName: String)(implicit effect: Sync[F]): F[TransitionExecution] = diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala new file mode 100644 index 000000000..d371c8e45 --- /dev/null +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala @@ -0,0 +1,15 @@ +package com.ing.baker.runtime.model.recipeinstance + +import java.time.Duration +import java.util.Optional + +case class RecipeInstanceConfig( + idleTTL: Optional[Duration] = Optional.of(Duration.ofSeconds(5)), + ingredientsFilter: java.util.List[String] = java.util.List.of() + ) { + + def withIdleTTL(idleTTL: Optional[Duration]) = copy(idleTTL = idleTTL) + + def withIngredientsFilter(ingredientsFilter: java.util.List[String]) = + copy(ingredientsFilter = ingredientsFilter) +} \ No newline at end of file diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala similarity index 99% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala index 3003a2a18..2ec245880 100644 --- a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala +++ b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala @@ -41,6 +41,8 @@ trait Baker extends common.Baker[Future] with ScalaApi { override type DurationType = FiniteDuration + override type UnitType = Unit + @deprecated("This method is deprecated and will be removed after December 1st, 2026. Please use fireSensoryEventAndAwaitReceived instead.", "5.1.0") override def fireEventAndResolveWhenReceived(recipeInstanceId: String, event: EventInstance): Future[SensoryEventStatus] = fireEventAndResolveWhenReceived(recipeInstanceId, event, None) diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala diff --git a/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala b/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala similarity index 100% rename from core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala rename to core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java diff --git a/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java b/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java similarity index 100% rename from core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java rename to core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java diff --git a/core/baker-interface/src/test/resources/logback-test.xml b/core/baker-runtime/src/test/resources/logback-test.xml similarity index 100% rename from core/baker-interface/src/test/resources/logback-test.xml rename to core/baker-runtime/src/test/resources/logback-test.xml diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala similarity index 86% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala index 44a217f82..3c732abe8 100644 --- a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala +++ b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala @@ -36,13 +36,13 @@ trait BakerModelSpecSetupTests { test("providing implementations in a sequence") { context => for { - baker <- context.setupBakerWithNoRecipe(mockImplementations) + _ <- context.setupBakerWithNoRecipe(mockImplementations) } yield succeed } - test("providing an implementation with the class simplename same as the interaction") { context => + test("providing an implementation with the class simple name same as the interaction") { context => for { - baker <- context.setupBakerWithNoRecipe(mockImplementations) + _ <- context.setupBakerWithNoRecipe(mockImplementations) } yield succeed } @@ -96,7 +96,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(mockImplementations) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe NonProvidedIngredient:68b775e508fc6877 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction")) + case Left(e) => e should have(Symbol("message")("Recipe NonProvidedIngredient:68b775e508fc6877 has validation errors: Ingredient 'initialIngredient' for interaction 'InteractionOne' is not provided by any event or interaction")) case Right(_) => fail("Adding a recipe should fail") } } yield succeed @@ -111,7 +111,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List.empty) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe MissingImplementation:dc3970efc8837e64 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) + case Left(e) => e should have(Symbol("message")("Recipe MissingImplementation:dc3970efc8837e64 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(NameNotFound)")) case Right(_) => fail("Adding a recipe should fail") } } yield succeed @@ -126,7 +126,7 @@ trait BakerModelSpecSetupTests { for { baker <- context.buildBaker(List(InteractionInstance.unsafeFrom(new InteractionOneWrongApply()))) _ <- baker.addRecipe(RecipeRecord.of(RecipeCompiler.compileRecipe(recipe))).attempt.map { - case Left(e) => e should have('message("Recipe WrongImplementation:8e2745de0bb0bde5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) + case Left(e) => e should have(Symbol("message")("Recipe WrongImplementation:8e2745de0bb0bde5 has implementation errors: No compatible implementation provided for interaction: InteractionOne: List(InteractionOne input size differs: transition expects 2, implementation provides 1)")) case Right(_) => fail("Adding an interaction should fail") } } yield succeed diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala diff --git a/core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala b/core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala rename to core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala diff --git a/core/baker-interface/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala b/core/baker-runtime/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala similarity index 100% rename from core/baker-interface/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala rename to core/baker-runtime/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala diff --git a/core/baker-test/pom.xml b/core/baker-test/pom.xml index 639456734..b7b915390 100644 --- a/core/baker-test/pom.xml +++ b/core/baker-test/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-interface + baker-runtime ${project.version} diff --git a/examples/baker-example/pom.xml b/examples/baker-example/pom.xml index 5ddda1732..9c86fb3de 100644 --- a/examples/baker-example/pom.xml +++ b/examples/baker-example/pom.xml @@ -31,6 +31,11 @@ baker-compiler ${project.version} + + com.ing.baker + baker-runtime-in-memory + ${project.version} + org.scala-lang diff --git a/examples/docs-code-snippets/pom.xml b/examples/docs-code-snippets/pom.xml index f0f5a1858..71a73aa03 100644 --- a/examples/docs-code-snippets/pom.xml +++ b/examples/docs-code-snippets/pom.xml @@ -23,7 +23,7 @@ com.ing.baker - baker-interface-kotlin + baker-runtime-kotlin ${project.version} diff --git a/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala b/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala index af5af6431..7689c4122 100644 --- a/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala +++ b/examples/docs-code-snippets/src/main/scala/examples/scala/application/WebShopApp.scala @@ -4,7 +4,7 @@ import cats.effect.IO import cats.effect.unsafe.implicits.global import com.ing.baker.compiler.RecipeCompiler import com.ing.baker.runtime.inmemory.InMemoryBaker -import com.ing.baker.runtime.model.InteractionInstance +import com.ing.baker.runtime.model.{BakerF, InteractionInstance} import com.ing.baker.runtime.scaladsl.EventInstance import examples.scala.events.OrderPlaced import examples.scala.ingredients.Address @@ -13,17 +13,22 @@ import examples.scala.recipes.WebShopRecipe import java.util.UUID import scala.concurrent.duration.DurationInt +import scala.reflect.ClassTag class WebShopApp { def main(args: Array[String]): Unit = { + // Required implicits for InteractionInstance.unsafeFrom[IO] + implicit val ioClassTag: ClassTag[IO[Any]] = ClassTag(classOf[IO[_]]) + val interactions = List( new CancelOrderImpl(), new ShipOrderImpl(), new CheckStockImpl(), ) - val bakerF = InMemoryBaker.build(implementations = interactions.map(InteractionInstance.unsafeFrom[IO])) + val bakerF = InMemoryBaker.build(interactions.map(InteractionInstance.unsafeFrom[IO])) + .asInstanceOf[IO[BakerF[IO]]] .unsafeRunSync() val recipeInstanceId = UUID.randomUUID().toString diff --git a/http/baker-http-client/pom.xml b/http/baker-http-client/pom.xml index e2ad5bed1..eb4f4c156 100644 --- a/http/baker-http-client/pom.xml +++ b/http/baker-http-client/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-interface + baker-runtime ${project.version} diff --git a/http/baker-http-server/pom.xml b/http/baker-http-server/pom.xml index 43d8c0ccd..ac059daf0 100644 --- a/http/baker-http-server/pom.xml +++ b/http/baker-http-server/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-interface + baker-runtime ${project.version} diff --git a/pom.xml b/pom.xml index c84b18bac..fdcf8d710 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ 2.10.17 4.17.0 2.13.0 - 3.6.3 + 3.7.0 3.1.3 0.14.4 0.14.14 @@ -44,7 +44,7 @@ 2.14.0 3.0.2 4.2.0 - 2.2.20 + 2.3.21 1.10.2 1.10.0 1.5.32 @@ -107,8 +107,9 @@ core/baker-types core/baker-intermediate-language - core/baker-interface - core/baker-interface-kotlin + core/baker-runtime + core/baker-runtime-kotlin + core/baker-runtime-in-memory core/baker-annotations core/baker-recipe-dsl core/baker-recipe-dsl-kotlin From d3f53790ba106fea4d7b25d5b0826d0f20d5649a Mon Sep 17 00:00:00 2001 From: Dragoslav Pavkovic Date: Tue, 23 Jun 2026 13:03:24 +0200 Subject: [PATCH 16/16] renamimg back runtime to interface --- bakery/bakery-interaction-protocol/pom.xml | 2 +- core/baker-akka-runtime/pom.xml | 2 +- .../pom.xml | 6 +++--- .../kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt | 0 .../com/ing/baker/runtime/inmemory/InMemoryEventStream.kt | 0 .../baker/runtime/inmemory/InMemoryInteractionManager.kt | 0 .../baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt | 0 .../com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt | 0 .../com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala | 0 .../baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala | 0 .../pom.xml | 4 ++-- .../main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt | 0 .../ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt | 0 .../com/ing/baker/runtime/kotlindsl/EventResolutions.kt | 0 .../baker/runtime/kotlindsl/FunctionInteractionInstance.kt | 0 .../kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt | 0 .../src/test/kotlin/BakerInterfaceTest.kt | 0 .../src/test/kotlin/FunctionInteractionInstanceTest.kt | 0 core/{baker-runtime => baker-interface}/pom.xml | 2 +- .../src/main/protobuf/common.proto | 0 .../src/main/scala/com/ing/baker/runtime/common/Baker.scala | 0 .../scala/com/ing/baker/runtime/common/BakerEvent.scala | 0 .../scala/com/ing/baker/runtime/common/BakerException.scala | 0 .../scala/com/ing/baker/runtime/common/EventInstance.scala | 0 .../scala/com/ing/baker/runtime/common/EventMoment.scala | 0 .../com/ing/baker/runtime/common/EventResolutions.scala | 0 .../com/ing/baker/runtime/common/IngredientInstance.scala | 0 .../runtime/common/InteractionExecutionFailureReason.java | 0 .../baker/runtime/common/InteractionExecutionResult.scala | 0 .../com/ing/baker/runtime/common/InteractionInstance.scala | 0 .../runtime/common/InteractionInstanceDescriptor.scala | 0 .../ing/baker/runtime/common/InteractionInstanceInput.scala | 0 .../ing/baker/runtime/common/LanguageDataStructures.scala | 0 .../com/ing/baker/runtime/common/RecipeEventMetadata.scala | 0 .../com/ing/baker/runtime/common/RecipeInformation.scala | 0 .../ing/baker/runtime/common/RecipeInstanceMetadata.scala | 0 .../com/ing/baker/runtime/common/RecipeInstanceState.scala | 0 .../scala/com/ing/baker/runtime/common/RejectReason.java | 0 .../com/ing/baker/runtime/common/SensoryEventResult.scala | 0 .../com/ing/baker/runtime/common/SensoryEventStatus.java | 0 .../src/main/scala/com/ing/baker/runtime/common/Utils.scala | 0 .../main/scala/com/ing/baker/runtime/common/package.scala | 0 .../runtime/defaultinteractions/TimerInteraction.scala | 0 .../runtime/defaultinteractions/TimerInteractionJava.scala | 0 .../com/ing/baker/runtime/defaultinteractions/package.scala | 0 .../main/scala/com/ing/baker/runtime/javadsl/Baker.scala | 0 .../scala/com/ing/baker/runtime/javadsl/BakerCommon.scala | 0 .../scala/com/ing/baker/runtime/javadsl/BakerEvent.scala | 0 .../scala/com/ing/baker/runtime/javadsl/EventInstance.scala | 0 .../scala/com/ing/baker/runtime/javadsl/EventListener.scala | 0 .../scala/com/ing/baker/runtime/javadsl/EventMoment.scala | 0 .../com/ing/baker/runtime/javadsl/EventResolutions.scala | 0 .../com/ing/baker/runtime/javadsl/IngredientInstance.scala | 0 .../baker/runtime/javadsl/InteractionExecutionResult.scala | 0 .../com/ing/baker/runtime/javadsl/InteractionInstance.scala | 0 .../runtime/javadsl/InteractionInstanceDescriptor.scala | 0 .../baker/runtime/javadsl/InteractionInstanceInput.scala | 0 .../com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala | 0 .../com/ing/baker/runtime/javadsl/RecipeInformation.scala | 0 .../ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala | 0 .../com/ing/baker/runtime/javadsl/RecipeInstanceState.scala | 0 .../com/ing/baker/runtime/javadsl/SensoryEventResult.scala | 0 .../scala/com/ing/baker/runtime/model/BakerComponents.scala | 0 .../scala/com/ing/baker/runtime/model/BakerConfig.scala | 0 .../src/main/scala/com/ing/baker/runtime/model/BakerF.scala | 0 .../scala/com/ing/baker/runtime/model/BakerLogging.scala | 0 .../scala/com/ing/baker/runtime/model/EventStream.scala | 0 .../ing/baker/runtime/model/FireSensoryEventRejection.scala | 0 .../com/ing/baker/runtime/model/InteractionInstance.scala | 0 .../com/ing/baker/runtime/model/InteractionManager.scala | 0 .../com/ing/baker/runtime/model/RecipeInstanceManager.scala | 0 .../scala/com/ing/baker/runtime/model/RecipeManager.scala | 0 .../baker/runtime/model/recipeinstance/RecipeInstance.scala | 0 .../runtime/model/recipeinstance/RecipeInstanceConfig.scala | 0 .../recipeinstance/RecipeInstanceEventValidation.scala | 0 .../runtime/model/recipeinstance/RecipeInstanceState.scala | 0 .../runtime/model/recipeinstance/TransitionExecution.scala | 0 .../main/scala/com/ing/baker/runtime/scaladsl/Baker.scala | 0 .../scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala | 0 .../scala/com/ing/baker/runtime/scaladsl/BakerResult.scala | 0 .../com/ing/baker/runtime/scaladsl/EventInstance.scala | 0 .../scala/com/ing/baker/runtime/scaladsl/EventMoment.scala | 0 .../com/ing/baker/runtime/scaladsl/EventResolutions.scala | 0 .../com/ing/baker/runtime/scaladsl/EventResolutionsF.scala | 0 .../com/ing/baker/runtime/scaladsl/IngredientInstance.scala | 0 .../baker/runtime/scaladsl/InteractionExecutionResult.scala | 0 .../ing/baker/runtime/scaladsl/InteractionInstance.scala | 0 .../runtime/scaladsl/InteractionInstanceDescriptor.scala | 0 .../baker/runtime/scaladsl/InteractionInstanceInput.scala | 0 .../ing/baker/runtime/scaladsl/RecipeEventMetadata.scala | 0 .../com/ing/baker/runtime/scaladsl/RecipeInformation.scala | 0 .../ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala | 0 .../ing/baker/runtime/scaladsl/RecipeInstanceState.scala | 0 .../com/ing/baker/runtime/scaladsl/SensoryEventResult.scala | 0 .../baker/runtime/serialization/AddMetaDataRequest.scala | 0 .../com/ing/baker/runtime/serialization/BakeRequest.scala | 0 .../com/ing/baker/runtime/serialization/Encryption.scala | 0 .../baker/runtime/serialization/InteractionExecution.scala | 0 .../com/ing/baker/runtime/serialization/JsonCodec.scala | 0 .../com/ing/baker/runtime/serialization/JsonDecoders.scala | 0 .../com/ing/baker/runtime/serialization/JsonEncoders.scala | 0 .../com/ing/baker/runtime/serialization/ProtoMap.scala | 0 .../ing/baker/runtime/serialization/TokenIdentifier.scala | 0 .../serialization/protomappings/BakerEventMapping.scala | 0 .../serialization/protomappings/BakerExceptionMapping.scala | 0 .../serialization/protomappings/BakerTypesMapping.scala | 0 .../serialization/protomappings/BakerValuesMapping.scala | 0 .../serialization/protomappings/CompiledRecipeMapping.scala | 0 .../protomappings/EventDescriptorMapping.scala | 0 .../serialization/protomappings/EventMomentMapping.scala | 0 .../protomappings/EventOutputTransformerMapping.scala | 0 .../protomappings/IngredientDescriptorMapping.scala | 0 .../protomappings/IngredientInstanceMapping.scala | 0 .../protomappings/InteractionFailureStrategyMapping.scala | 0 .../serialization/protomappings/ProcessStateMapping.scala | 0 .../protomappings/RecipeEventMetadataMapping.scala | 0 .../protomappings/RecipeInformationMapping.scala | 0 .../protomappings/RecipeInstanceMetadataMapping.scala | 0 .../serialization/protomappings/RuntimeEventMapping.scala | 0 .../protomappings/SensoryEventResultMapping.scala | 0 .../protomappings/SensoryEventStatusMappingHelper.scala | 0 .../com/ing/baker/runtime/javadsl/AnnotatedInteraction.java | 0 .../ing/baker/runtime/javadsl/AnnotatedInteraction2.java | 0 .../ing/baker/runtime/javadsl/AnnotatedInteraction3.java | 0 .../baker/runtime/javadsl/AnnotatedParentInteraction.java | 0 .../com/ing/baker/runtime/javadsl/MultipleApplyMethods.java | 0 .../com/ing/baker/runtime/javadsl/NamedInteraction.java | 0 .../com/ing/baker/runtime/javadsl/NamedInteraction2.java | 0 .../com/ing/baker/runtime/javadsl/NamedInteraction3.java | 0 .../java/com/ing/baker/runtime/javadsl/NoApplyMethod.java | 0 .../baker/runtime/javadsl/ParentInteractionWithName.java | 0 .../baker/runtime/javadsl/ParentInteractionWithoutName.java | 0 .../java/com/ing/baker/runtime/javadsl/SimpleEvent.java | 0 .../com/ing/baker/runtime/javadsl/SimpleInteraction.java | 0 .../src/test/resources/logback-test.xml | 0 .../ing/baker/runtime/javadsl/InteractionInstanceTest.scala | 0 .../com/ing/baker/runtime/model/BakerModelFixtures.scala | 0 .../scala/com/ing/baker/runtime/model/BakerModelSpec.scala | 0 .../baker/runtime/model/BakerModelSpecEdgeCasesTests.scala | 0 .../baker/runtime/model/BakerModelSpecEnquireTests.scala | 0 .../model/BakerModelSpecExecutionSemanticsTests.scala | 0 .../baker/runtime/model/BakerModelSpecFailureTests.scala | 0 .../ing/baker/runtime/model/BakerModelSpecSetupTests.scala | 0 .../com/ing/baker/runtime/model/RuntimeEventMatcher.scala | 0 .../test/scala/com/ing/baker/runtime/model/TestRecipe.scala | 0 .../baker/runtime/scaladsl/InteractionInstanceTest.scala | 0 .../com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala | 0 .../ing/baker/runtime/serialization/JsonDecodersSpec.scala | 0 .../ing/baker/runtime/serialization/JsonEncodersSpec.scala | 0 .../src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala | 0 core/baker-test/pom.xml | 2 +- examples/baker-example/pom.xml | 2 +- examples/docs-code-snippets/pom.xml | 2 +- http/baker-http-client/pom.xml | 2 +- http/baker-http-server/pom.xml | 2 +- pom.xml | 6 +++--- 156 files changed, 16 insertions(+), 16 deletions(-) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/pom.xml (98%) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt (100%) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt (100%) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt (100%) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt (100%) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt (100%) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala (100%) rename core/{baker-runtime-in-memory => baker-interface-in-memory}/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala (100%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/pom.xml (97%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt (100%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt (100%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt (100%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt (100%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt (100%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/src/test/kotlin/BakerInterfaceTest.kt (100%) rename core/{baker-runtime-kotlin => baker-interface-kotlin}/src/test/kotlin/FunctionInteractionInstanceTest.kt (100%) rename core/{baker-runtime => baker-interface}/pom.xml (99%) rename core/{baker-runtime => baker-interface}/src/main/protobuf/common.proto (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/Baker.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/BakerException.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/RejectReason.java (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/Utils.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/common/package.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/BakerF.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/EventStream.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala (100%) rename core/{baker-runtime => baker-interface}/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java (100%) rename core/{baker-runtime => baker-interface}/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java (100%) rename core/{baker-runtime => baker-interface}/src/test/resources/logback-test.xml (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala (100%) rename core/{baker-runtime => baker-interface}/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala (100%) diff --git a/bakery/bakery-interaction-protocol/pom.xml b/bakery/bakery-interaction-protocol/pom.xml index 26eab174f..6c6e120dd 100644 --- a/bakery/bakery-interaction-protocol/pom.xml +++ b/bakery/bakery-interaction-protocol/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-runtime + baker-interface ${project.version} diff --git a/core/baker-akka-runtime/pom.xml b/core/baker-akka-runtime/pom.xml index 5eb08e625..4ceeca6ab 100644 --- a/core/baker-akka-runtime/pom.xml +++ b/core/baker-akka-runtime/pom.xml @@ -33,7 +33,7 @@ com.ing.baker - baker-runtime + baker-interface ${project.version} diff --git a/core/baker-runtime-in-memory/pom.xml b/core/baker-interface-in-memory/pom.xml similarity index 98% rename from core/baker-runtime-in-memory/pom.xml rename to core/baker-interface-in-memory/pom.xml index 04f48b571..f75e04231 100644 --- a/core/baker-runtime-in-memory/pom.xml +++ b/core/baker-interface-in-memory/pom.xml @@ -11,14 +11,14 @@ ../../pom.xml - baker-runtime-in-memory + baker-interface-in-memory Baker Runtime In-Memory In-Memory Baker runtime definitions and core API com.ing.baker - baker-runtime + baker-interface ${project.version} @@ -99,7 +99,7 @@ com.ing.baker - baker-runtime + baker-interface ${project.version} test-jar test diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt b/core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt similarity index 100% rename from core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt rename to core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryBaker.kt diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt b/core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt similarity index 100% rename from core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt rename to core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryEventStream.kt diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt b/core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt similarity index 100% rename from core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt rename to core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryInteractionManager.kt diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt b/core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt similarity index 100% rename from core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt rename to core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeInstanceManager.kt diff --git a/core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt b/core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt similarity index 100% rename from core/baker-runtime-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt rename to core/baker-interface-in-memory/src/main/kotlin/com/ing/baker/runtime/inmemory/InMemoryRecipeManager.kt diff --git a/core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala b/core/baker-interface-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala similarity index 100% rename from core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala rename to core/baker-interface-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryBakerSpec.scala diff --git a/core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala b/core/baker-interface-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala similarity index 100% rename from core/baker-runtime-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala rename to core/baker-interface-in-memory/src/test/scala/com/ing/baker/runtime/inmemory/InMemoryMemoryCleanupSpec.scala diff --git a/core/baker-runtime-kotlin/pom.xml b/core/baker-interface-kotlin/pom.xml similarity index 97% rename from core/baker-runtime-kotlin/pom.xml rename to core/baker-interface-kotlin/pom.xml index 6f742738b..bdd2bb1c9 100644 --- a/core/baker-runtime-kotlin/pom.xml +++ b/core/baker-interface-kotlin/pom.xml @@ -11,14 +11,14 @@ ../../pom.xml - baker-runtime-kotlin + baker-interface-kotlin Baker Runtime Kotlin Kotlin runtime for Baker com.ing.baker - baker-runtime + baker-interface ${project.version} diff --git a/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt similarity index 100% rename from core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt rename to core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/Baker.kt diff --git a/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt similarity index 100% rename from core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt rename to core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/ClassInteractionInstance.kt diff --git a/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt similarity index 100% rename from core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt rename to core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/EventResolutions.kt diff --git a/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt similarity index 100% rename from core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt rename to core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/FunctionInteractionInstance.kt diff --git a/core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt b/core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt similarity index 100% rename from core/baker-runtime-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt rename to core/baker-interface-kotlin/src/main/kotlin/com/ing/baker/runtime/kotlindsl/InMemoryBaker.kt diff --git a/core/baker-runtime-kotlin/src/test/kotlin/BakerInterfaceTest.kt b/core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt similarity index 100% rename from core/baker-runtime-kotlin/src/test/kotlin/BakerInterfaceTest.kt rename to core/baker-interface-kotlin/src/test/kotlin/BakerInterfaceTest.kt diff --git a/core/baker-runtime-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt b/core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt similarity index 100% rename from core/baker-runtime-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt rename to core/baker-interface-kotlin/src/test/kotlin/FunctionInteractionInstanceTest.kt diff --git a/core/baker-runtime/pom.xml b/core/baker-interface/pom.xml similarity index 99% rename from core/baker-runtime/pom.xml rename to core/baker-interface/pom.xml index 187ee91bb..12e0c5283 100644 --- a/core/baker-runtime/pom.xml +++ b/core/baker-interface/pom.xml @@ -11,7 +11,7 @@ ../../pom.xml - baker-runtime + baker-interface Baker Runtime Baker runtime definitions and core API diff --git a/core/baker-runtime/src/main/protobuf/common.proto b/core/baker-interface/src/main/protobuf/common.proto similarity index 100% rename from core/baker-runtime/src/main/protobuf/common.proto rename to core/baker-interface/src/main/protobuf/common.proto diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Baker.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Baker.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Baker.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Baker.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerEvent.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerException.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerException.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/BakerException.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/BakerException.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventMoment.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/EventResolutions.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/IngredientInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionFailureReason.java diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionExecutionResult.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceDescriptor.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/InteractionInstanceInput.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/LanguageDataStructures.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeEventMetadata.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInformation.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceMetadata.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RecipeInstanceState.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RejectReason.java b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RejectReason.java similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/RejectReason.java rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/RejectReason.java diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventResult.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/SensoryEventStatus.java diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Utils.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Utils.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/Utils.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/Utils.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/package.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/common/package.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/common/package.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/common/package.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteraction.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/TimerInteractionJava.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/defaultinteractions/package.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/Baker.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerCommon.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/BakerEvent.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventListener.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventMoment.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/EventResolutions.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/IngredientInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionExecutionResult.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceDescriptor.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/InteractionInstanceInput.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeEventMetadata.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInformation.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceMetadata.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/RecipeInstanceState.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/javadsl/SensoryEventResult.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerComponents.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerConfig.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerF.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerF.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerF.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerF.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/BakerLogging.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/EventStream.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/EventStream.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/EventStream.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/EventStream.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/FireSensoryEventRejection.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/InteractionManager.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeInstanceManager.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/RecipeManager.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceConfig.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceEventValidation.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/RecipeInstanceState.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/model/recipeinstance/TransitionExecution.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/Baker.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerEvent.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/BakerResult.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventMoment.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutions.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/EventResolutionsF.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/IngredientInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionExecutionResult.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstance.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceDescriptor.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceInput.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeEventMetadata.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInformation.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceMetadata.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/RecipeInstanceState.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/scaladsl/SensoryEventResult.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/AddMetaDataRequest.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/BakeRequest.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/Encryption.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/InteractionExecution.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonCodec.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonDecoders.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/JsonEncoders.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/ProtoMap.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/TokenIdentifier.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerEventMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerExceptionMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerTypesMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/BakerValuesMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/CompiledRecipeMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventDescriptorMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventMomentMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/EventOutputTransformerMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientDescriptorMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/IngredientInstanceMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/InteractionFailureStrategyMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/ProcessStateMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeEventMetadataMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInformationMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RecipeInstanceMetadataMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/RuntimeEventMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventResultMapping.scala diff --git a/core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala b/core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala similarity index 100% rename from core/baker-runtime/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala rename to core/baker-interface/src/main/scala/com/ing/baker/runtime/serialization/protomappings/SensoryEventStatusMappingHelper.scala diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction2.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedInteraction3.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/AnnotatedParentInteraction.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/MultipleApplyMethods.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction2.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NamedInteraction3.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/NoApplyMethod.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithName.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/ParentInteractionWithoutName.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleEvent.java diff --git a/core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java b/core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java similarity index 100% rename from core/baker-runtime/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java rename to core/baker-interface/src/test/java/com/ing/baker/runtime/javadsl/SimpleInteraction.java diff --git a/core/baker-runtime/src/test/resources/logback-test.xml b/core/baker-interface/src/test/resources/logback-test.xml similarity index 100% rename from core/baker-runtime/src/test/resources/logback-test.xml rename to core/baker-interface/src/test/resources/logback-test.xml diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/javadsl/InteractionInstanceTest.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelFixtures.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpec.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEdgeCasesTests.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecEnquireTests.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecExecutionSemanticsTests.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecFailureTests.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/BakerModelSpecSetupTests.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/RuntimeEventMatcher.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/model/TestRecipe.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/InteractionInstanceTest.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/scaladsl/ScalaDSLRuntime.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonDecodersSpec.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala b/core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala rename to core/baker-interface/src/test/scala/com/ing/baker/runtime/serialization/JsonEncodersSpec.scala diff --git a/core/baker-runtime/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala b/core/baker-interface/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala similarity index 100% rename from core/baker-runtime/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala rename to core/baker-interface/src/test/scala/com/ing/bakery/utils/BakeryFunSpec.scala diff --git a/core/baker-test/pom.xml b/core/baker-test/pom.xml index b7b915390..639456734 100644 --- a/core/baker-test/pom.xml +++ b/core/baker-test/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-runtime + baker-interface ${project.version} diff --git a/examples/baker-example/pom.xml b/examples/baker-example/pom.xml index 9c86fb3de..ae1205241 100644 --- a/examples/baker-example/pom.xml +++ b/examples/baker-example/pom.xml @@ -33,7 +33,7 @@ com.ing.baker - baker-runtime-in-memory + baker-interface-in-memory ${project.version} diff --git a/examples/docs-code-snippets/pom.xml b/examples/docs-code-snippets/pom.xml index 71a73aa03..f0f5a1858 100644 --- a/examples/docs-code-snippets/pom.xml +++ b/examples/docs-code-snippets/pom.xml @@ -23,7 +23,7 @@ com.ing.baker - baker-runtime-kotlin + baker-interface-kotlin ${project.version} diff --git a/http/baker-http-client/pom.xml b/http/baker-http-client/pom.xml index eb4f4c156..e2ad5bed1 100644 --- a/http/baker-http-client/pom.xml +++ b/http/baker-http-client/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-runtime + baker-interface ${project.version} diff --git a/http/baker-http-server/pom.xml b/http/baker-http-server/pom.xml index ac059daf0..43d8c0ccd 100644 --- a/http/baker-http-server/pom.xml +++ b/http/baker-http-server/pom.xml @@ -18,7 +18,7 @@ com.ing.baker - baker-runtime + baker-interface ${project.version} diff --git a/pom.xml b/pom.xml index fdcf8d710..79d9043a0 100644 --- a/pom.xml +++ b/pom.xml @@ -107,9 +107,9 @@ core/baker-types core/baker-intermediate-language - core/baker-runtime - core/baker-runtime-kotlin - core/baker-runtime-in-memory + core/baker-interface + core/baker-interface-kotlin + core/baker-interface-in-memory core/baker-annotations core/baker-recipe-dsl core/baker-recipe-dsl-kotlin