diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..7ba97546 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{kt,kts}] +ktlint_code_style = intellij_idea +max_line_length = off +ij_kotlin_packages_to_use_import_on_demand = io.ktor.** + +[**/runtime/path/example/**] +ktlint = disabled + +[*.{yml,yaml,json}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/build-backend.yml b/.github/workflows/build-backend.yml index e401e0ee..bd3a4fca 100644 --- a/.github/workflows/build-backend.yml +++ b/.github/workflows/build-backend.yml @@ -66,14 +66,17 @@ jobs: java-version: '21' distribution: 'temurin' - # Step 4: Run Gradle build + # Step 4: Quality gate — ktlint formatting + detekt static analysis. + # `--continue` so a single run collects EVERY finding across all modules and both tools + # (Gradle otherwise aborts on the first failing task, hiding later modules/detekt). The build + # still fails at the end if anything failed. + - name: Run quality gate (ktlint + detekt) + run: ./gradlew lintKotlin detekt --continue + + # Step 5: Run Gradle build (compiles all modules and runs unit + integration tests; - name: Run Gradle Build run: ./gradlew build --warning-mode all - # Step 5: Run Detekt static analysis - - name: Run Detekt - run: ./gradlew detekt - # Step 6: Enforce per-class test coverage (≥ 75%) for modules with in-process tests - name: Check Coverage run: ./gradlew :bpmn-to-code-core:jacocoTestCoverageVerification :bpmn-to-code-web:jacocoTestCoverageVerification :bpmn-to-code-testing:jacocoTestCoverageVerification :bpmn-to-code-runtime:jacocoTestCoverageVerification diff --git a/CLAUDE.md b/CLAUDE.md index 1e686f61..eb0414f8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,6 +62,17 @@ lefthook install ./gradlew :bpmn-to-code-maven:test ``` +### Code Quality (ktlint + detekt) +Kotlin quality is enforced by ktlint (formatting/imports) and detekt (semantic/structural). Both are +wired into `check`/`build` and gate CI + the pre-push hook. +```bash +./gradlew lintKotlin # ktlint check +./gradlew formatKotlin # ktlint auto-fix +./gradlew detekt # detekt +``` +No baseline and no silent suppressions — fix findings or add a scoped exception in the relevant +config. ktlint config lives in `.editorconfig`, detekt config in `config/detekt/detekt.yml`. + ### Plugin Development The plugins generate code from BPMN files. Key configuration parameters: - `filePattern`: BPMN file location pattern diff --git a/bpmn-to-code-architecture-tests/src/test/kotlin/io/miragon/bpmn/architecture/ExternalModuleImportTest.kt b/bpmn-to-code-architecture-tests/src/test/kotlin/io/miragon/bpmn/architecture/ExternalModuleImportTest.kt index 11369789..77141450 100644 --- a/bpmn-to-code-architecture-tests/src/test/kotlin/io/miragon/bpmn/architecture/ExternalModuleImportTest.kt +++ b/bpmn-to-code-architecture-tests/src/test/kotlin/io/miragon/bpmn/architecture/ExternalModuleImportTest.kt @@ -17,7 +17,7 @@ import org.junit.jupiter.params.provider.ValueSource class ExternalModuleImportTest { private val forbiddenImportPrefixes = listOf( - "io.miragon.bpmn.application.", // services and ports + "io.miragon.bpmn.application.", // services and ports "io.miragon.bpmn.adapter.outbound.", // out-adapters ) @@ -27,7 +27,7 @@ class ExternalModuleImportTest { "bpmn-to-code-gradle", "bpmn-to-code-maven", "bpmn-to-code-web", - ] + ], ) fun `plugin module only imports domain objects or inbound adapters from core`(modulePath: String) { Konsist diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPlugin.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPlugin.kt index d751e475..d41d23bd 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPlugin.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPlugin.kt @@ -28,6 +28,6 @@ class CreateProcessApiFilesystemPlugin( outputLanguage = outputLanguage, engine = engine, validationConfig = validationConfig, - ) + ), ) -} \ No newline at end of file +} diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPlugin.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPlugin.kt index 5b8f2f35..22811488 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPlugin.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPlugin.kt @@ -17,22 +17,20 @@ class CreateProcessApiInMemoryPlugin( outputLanguage: OutputLanguage, engine: ProcessEngine, validationConfig: ValidationConfig = ValidationConfig(), - ): List { - return useCase.generateProcessApi( - GenerateProcessApiInMemoryUseCase.Command( - packagePath = packagePath, - outputLanguage = outputLanguage, - engine = engine, - validationConfig = validationConfig, - bpmnContents = bpmnContents.map { - GenerateProcessApiInMemoryUseCase.BpmnInput( - bpmnXml = it.bpmnXml, - processName = it.processName - ) - }, - ) - ) - } + ): List = useCase.generateProcessApi( + GenerateProcessApiInMemoryUseCase.Command( + packagePath = packagePath, + outputLanguage = outputLanguage, + engine = engine, + validationConfig = validationConfig, + bpmnContents = bpmnContents.map { + GenerateProcessApiInMemoryUseCase.BpmnInput( + bpmnXml = it.bpmnXml, + processName = it.processName, + ) + }, + ), + ) data class BpmnInput( val bpmnXml: String, diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPlugin.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPlugin.kt index c53d6187..3dc9623b 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPlugin.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPlugin.kt @@ -22,6 +22,6 @@ class CreateProcessJsonFilesystemPlugin( outputFolderPath = outputFolderPath, engine = engine, validationConfig = validationConfig, - ) + ), ) } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPlugin.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPlugin.kt index 5121bb08..93681242 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPlugin.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPlugin.kt @@ -14,20 +14,18 @@ class CreateProcessJsonInMemoryPlugin( bpmnContents: List, engine: ProcessEngine, validationConfig: ValidationConfig = ValidationConfig(), - ): List { - return useCase.generateProcessJson( - GenerateProcessJsonInMemoryUseCase.Command( - engine = engine, - validationConfig = validationConfig, - bpmnContents = bpmnContents.map { - GenerateProcessJsonInMemoryUseCase.BpmnInput( - bpmnXml = it.bpmnXml, - processName = it.processName, - ) - }, - ) - ) - } + ): List = useCase.generateProcessJson( + GenerateProcessJsonInMemoryUseCase.Command( + engine = engine, + validationConfig = validationConfig, + bpmnContents = bpmnContents.map { + GenerateProcessJsonInMemoryUseCase.BpmnInput( + bpmnXml = it.bpmnXml, + processName = it.processName, + ) + }, + ), + ) data class BpmnInput( val bpmnXml: String, diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPlugin.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPlugin.kt index cf31ab80..ce9c9c05 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPlugin.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPlugin.kt @@ -21,6 +21,6 @@ class ValidateBpmnFilesystemPlugin( filePattern = filePattern, engine = engine, validationConfig = validationConfig, - ) + ), ) } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/ApiObjectSelection.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/ApiObjectSelection.kt index 6f8c0c9e..cf018fdc 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/ApiObjectSelection.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/ApiObjectSelection.kt @@ -18,9 +18,7 @@ internal object ApiObjectSelection { /** * Whether [type] has anything to contribute for [modelApi]. */ - fun includes(type: ApiObjectType, modelApi: BpmnModelApi): Boolean { - return type.hasContentIn(modelApi) - } + fun includes(type: ApiObjectType, modelApi: BpmnModelApi): Boolean = type.hasContentIn(modelApi) private fun ApiObjectType.hasContentIn(modelApi: BpmnModelApi): Boolean { val model = modelApi.model diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapter.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapter.kt index ef7b4614..571dd99b 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapter.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapter.kt @@ -26,8 +26,7 @@ internal class CodeGenerationAdapter( companion object { val processApiBuilders = mapOf( OutputLanguage.KOTLIN to KotlinProcessApiBuilder(), - OutputLanguage.JAVA to JavaProcessApiBuilder() + OutputLanguage.JAVA to JavaProcessApiBuilder(), ) } - } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/ApiConstants.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/ApiConstants.kt index 6e7dd1ad..d945b31a 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/ApiConstants.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/ApiConstants.kt @@ -9,6 +9,4 @@ import io.miragon.bpmn.domain.shared.VariableMapping * of them may legitimately share a name. The generated API has no such id — a name yields exactly one * constant — so the collapsing happens here, at the point where names become identifiers. */ -internal fun > List.asApiConstants(): List { - return filter { it.getRawName().isNotEmpty() }.distinctBy { it.getRawName() } -} +internal fun > List.asApiConstants(): List = filter { it.getRawName().isNotEmpty() }.distinctBy { it.getRawName() } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaNavigationWriter.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaNavigationWriter.kt index b4fba5c8..c16ed72d 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaNavigationWriter.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaNavigationWriter.kt @@ -51,7 +51,7 @@ internal class JavaNavigationWriter { classBuilder.superclass(ClassName.get(RUNTIME_PACKAGE, "AbstractFlowNode")) classBuilder.addMethod( MethodSpec.constructorBuilder().addModifiers(PUBLIC) - .addStatement("super(new \$T(\$S), \$S)", elementIdClass, node.id, node.elementType).build() + .addStatement("super(new \$T(\$S), \$S)", elementIdClass, node.id, node.elementType).build(), ) if (node.successors.isNotEmpty()) { classBuilder.addSuperinterface(navigableType(node)) @@ -72,7 +72,7 @@ internal class JavaNavigationWriter { private fun addInnerScope(classBuilder: TypeSpec.Builder, node: NavigationNode, inner: NavigationGraph) { // Qualify with the node so a bare `Inner`/`Next` doesn't bind to an enclosing scope's type. classBuilder.addSuperinterface( - ParameterizedTypeName.get(ClassName.get(RUNTIME_PACKAGE, "HasInnerScope"), ClassName.get("", node.objectName, "Inner")) + ParameterizedTypeName.get(ClassName.get(RUNTIME_PACKAGE, "HasInnerScope"), ClassName.get("", node.objectName, "Inner")), ) classBuilder.addMethod(innerMethod(node)) classBuilder.addType(buildInnerScope(node, inner.nodes.filter { it.isStart })) diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilder.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilder.kt index 2239fd93..ee64b090 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilder.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilder.kt @@ -1,15 +1,15 @@ package io.miragon.bpmn.adapter.outbound.codegen.builder -import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectSelection -import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectType import com.palantir.javapoet.ClassName import com.palantir.javapoet.CodeBlock import com.palantir.javapoet.FieldSpec import com.palantir.javapoet.JavaFile import com.palantir.javapoet.TypeSpec +import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectSelection +import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectType import io.miragon.bpmn.adapter.outbound.codegen.CodeGenerationAdapter -import io.miragon.bpmn.adapter.outbound.codegen.writer.ObjectWriter import io.miragon.bpmn.adapter.outbound.codegen.navigation.NavigationGraphFactory +import io.miragon.bpmn.adapter.outbound.codegen.writer.ObjectWriter import io.miragon.bpmn.domain.BpmnModelApi import io.miragon.bpmn.domain.GeneratedApiFile import io.miragon.bpmn.domain.ProcessModel.Variant @@ -102,7 +102,7 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB .addJavadoc( "BPMN element ids as declared in the source model.\n" + "Typically used in process-level tests or when searching for tasks.\n" + - "Worker runtime code rarely needs these.\n" + "Worker runtime code rarely needs these.\n", ) modelApi.model.allFlowNodes.sortedBy { it.getRawName() }.forEach { flowNode -> elementsBuilder.addField(createTypedAttribute(flowNode, elementIdClass)) @@ -156,7 +156,7 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB .addJavadoc( "Sequence flows between BPMN elements.\n" + "Mainly useful for process-model tooling, tests, and AI-agent consumers reasoning about the process shape.\n" + - "Worker code typically does not need these.\n" + "Worker code typically does not need these.\n", ) sequenceFlows.sortedBy { it.getRawName() }.forEach { flow -> val initCode = buildFlowInitializer(bpmnFlowClass, flow.id ?: "", flow.flowName, flow.sourceRef, flow.targetRef, flow.conditionExpression, flow.isDefault) @@ -189,7 +189,7 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB "Typed navigation over the process flow. Each element is a node exposing its {@code id}, " + "{@code elementType} and display {@code name}, plus the elements reachable from it as methods — " + "so a full path is verified by the compiler and offered by autocomplete. A subprocess's interior " + - "is its nested {@code Inner} scope.\n" + "is its nested {@code Inner} scope.\n", ) JavaNavigationWriter().write(relationsBuilder, NavigationGraphFactory.build(graph), staticAccessors = true) return relationsBuilder.build() @@ -201,7 +201,7 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB val callActivitiesBuilder = TypeSpec.classBuilder("CallActivities").addModifiers(PUBLIC, STATIC, FINAL) .addJavadoc( "Call activities grouped by element. Each nested class exposes the called {@code PROCESS_ID} plus " + - "the variable mappings passed into ({@code Inputs}) and returned from ({@code Outputs}) the called process.\n" + "the variable mappings passed into ({@code Inputs}) and returned from ({@code Outputs}) the called process.\n", ) modelApi.model.callActivities .sortedBy { it.getRawName() } @@ -215,7 +215,7 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB classBuilder.addField( FieldSpec.builder(processIdClass, "PROCESS_ID").addModifiers(PUBLIC, STATIC, FINAL) .initializer("new \$T(\$S)", processIdClass, callActivity.getValue()) - .build() + .build(), ) buildMappingsClass("Inputs", callActivity.inputMappings)?.let { classBuilder.addType(it) } buildMappingsClass("Outputs", callActivity.outputMappings)?.let { classBuilder.addType(it) } @@ -269,7 +269,7 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB val tasksBuilder = TypeSpec.classBuilder("ServiceTasks").addModifiers(PUBLIC, STATIC, FINAL) .addJavadoc( "Job worker task types used in {@code @JobWorker(type = ServiceTasks.X)} annotations.\n" + - "Kept as {@code public static final String} because annotation arguments must be compile-time constants.\n" + "Kept as {@code public static final String} because annotation arguments must be compile-time constants.\n", ) modelApi.model.serviceTasks.asApiConstants() .forEach { task -> tasksBuilder.addField(createAttribute(task)) } @@ -297,7 +297,7 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB .addJavadoc( "Process variables grouped by the BPMN element that declares them.\n" + "Direction is encoded in each variable's wrapper type: {@code VariableName.Input}, {@code VariableName.Output}, or {@code VariableName.InOut} when the variable is both read and written by the same element.\n" + - "Consumer APIs that take a specific subtype (for example, a method accepting {@code VariableName.Output}) get compile-time direction enforcement.\n" + "Consumer APIs that take a specific subtype (for example, a method accepting {@code VariableName.Output}) get compile-time direction enforcement.\n", ) val nodesWithVariables = modelApi.model.allFlowNodes .filter { it.variables.isNotEmpty() } @@ -384,17 +384,13 @@ internal class JavaProcessApiBuilder : CodeGenerationAdapter.AbstractProcessApiB } } - private fun createAttribute(variable: VariableMapping<*>): FieldSpec { - return FieldSpec.builder(String::class.java, variable.getName()) - .addModifiers(PUBLIC, STATIC, FINAL) - .initializer("\$S", variable.getValue()) - .build() - } + private fun createAttribute(variable: VariableMapping<*>): FieldSpec = FieldSpec.builder(String::class.java, variable.getName()) + .addModifiers(PUBLIC, STATIC, FINAL) + .initializer("\$S", variable.getValue()) + .build() - private fun createTypedAttribute(variable: VariableMapping, wrapperClass: ClassName): FieldSpec { - return FieldSpec.builder(wrapperClass, variable.getName()) - .addModifiers(PUBLIC, STATIC, FINAL) - .initializer("new \$T(\$S)", wrapperClass, variable.getValue()) - .build() - } + private fun createTypedAttribute(variable: VariableMapping, wrapperClass: ClassName): FieldSpec = FieldSpec.builder(wrapperClass, variable.getName()) + .addModifiers(PUBLIC, STATIC, FINAL) + .initializer("new \$T(\$S)", wrapperClass, variable.getValue()) + .build() } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinNavigationWriter.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinNavigationWriter.kt index 7fb3fbfe..4fc2549f 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinNavigationWriter.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinNavigationWriter.kt @@ -68,7 +68,7 @@ internal class KotlinNavigationWriter { private fun addInnerScope(nodeBuilder: TypeSpec.Builder, node: NavigationNode, inner: NavigationGraph) { // Qualify with the node so a bare `Inner`/`Next` doesn't bind to an enclosing scope's type. nodeBuilder.addSuperinterface( - ClassName(RUNTIME_PACKAGE, "HasInnerScope").parameterizedBy(ClassName("", node.objectName, "Inner")) + ClassName(RUNTIME_PACKAGE, "HasInnerScope").parameterizedBy(ClassName("", node.objectName, "Inner")), ) nodeBuilder.addFunction(innerFunction(node)) nodeBuilder.addType(buildInnerScope(node, inner.nodes.filter { it.isStart })) @@ -122,11 +122,9 @@ internal class KotlinNavigationWriter { return PropertySpec.builder("calledProcess", processIdClass).initializer("ProcessId(%S)", calledProcessId).build() } - private fun nodeAccessor(propertyName: String, objectName: String): PropertySpec { - return PropertySpec.builder(propertyName, ClassName("", objectName)) - .getter(FunSpec.getterBuilder().addStatement("return %N", objectName).build()) - .build() - } + private fun nodeAccessor(propertyName: String, objectName: String): PropertySpec = PropertySpec.builder(propertyName, ClassName("", objectName)) + .getter(FunSpec.getterBuilder().addStatement("return %N", objectName).build()) + .build() private companion object { private const val RUNTIME_PACKAGE = "io.miragon.bpmn.runtime" diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilder.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilder.kt index bc83cadc..d951c20a 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilder.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilder.kt @@ -1,7 +1,5 @@ package io.miragon.bpmn.adapter.outbound.codegen.builder -import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectSelection -import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectType import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock @@ -9,9 +7,11 @@ import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec +import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectSelection +import io.miragon.bpmn.adapter.outbound.codegen.ApiObjectType import io.miragon.bpmn.adapter.outbound.codegen.CodeGenerationAdapter -import io.miragon.bpmn.adapter.outbound.codegen.writer.ObjectWriter import io.miragon.bpmn.adapter.outbound.codegen.navigation.NavigationGraphFactory +import io.miragon.bpmn.adapter.outbound.codegen.writer.ObjectWriter import io.miragon.bpmn.domain.BpmnModelApi import io.miragon.bpmn.domain.GeneratedApiFile import io.miragon.bpmn.domain.ProcessModel.Variant @@ -105,7 +105,7 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp .addKdoc( "BPMN element ids as declared in the source model.\n" + "Typically used in process-level tests or when searching for tasks.\n" + - "Worker runtime code rarely needs these." + "Worker runtime code rarely needs these.", ) modelApi.model.allFlowNodes.sortedBy { it.getRawName() }.forEach { flowNode -> elementsBuilder.addProperty(createTypedAttribute(flowNode, elementIdClass)) @@ -159,7 +159,7 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp .addKdoc( "Sequence flows between BPMN elements.\n" + "Mainly useful for process-model tooling, tests, and AI-agent consumers reasoning about the process shape.\n" + - "Worker code typically does not need these." + "Worker code typically does not need these.", ) sequenceFlows.sortedBy { it.getRawName() }.forEach { flow -> val initStr = buildFlowInitializer(flow.id ?: "", flow.flowName, flow.sourceRef, flow.targetRef, flow.conditionExpression, flow.isDefault) @@ -168,20 +168,18 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp return flowsBuilder.build() } - private fun buildFlowInitializer(id: String, name: String?, sourceRef: String, targetRef: String, condition: String?, isDefault: Boolean): CodeBlock { - return CodeBlock.builder().apply { - add("BpmnFlow(\n") - indent() - add("id = %S,\n", id) - if (name != null) add("name = %S,\n", name) - add("sourceRef = %S,\n", sourceRef) - add("targetRef = %S,\n", targetRef) - if (condition != null) add("condition = %L,\n", stringLiteral(condition)) - if (isDefault) add("isDefault = true,\n") - unindent() - add(")") - }.build() - } + private fun buildFlowInitializer(id: String, name: String?, sourceRef: String, targetRef: String, condition: String?, isDefault: Boolean): CodeBlock = CodeBlock.builder().apply { + add("BpmnFlow(\n") + indent() + add("id = %S,\n", id) + if (name != null) add("name = %S,\n", name) + add("sourceRef = %S,\n", sourceRef) + add("targetRef = %S,\n", targetRef) + if (condition != null) add("condition = %L,\n", stringLiteral(condition)) + if (isDefault) add("isDefault = true,\n") + unindent() + add(")") + }.build() /** * Renders the process as a typed navigation graph: one nested object per element exposing its `id`, @@ -195,7 +193,7 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp "Each element is a node exposing its `id`, `elementType` and display `name`, plus the elements " + "reachable from it as named properties — so a full path is verified by the compiler and offered " + "by autocomplete. A subprocess's interior is its nested `Inner` scope.\n" + - "Intended for tooling, tests, and reasoning about the process shape." + "Intended for tooling, tests, and reasoning about the process shape.", ) KotlinNavigationWriter().write(relationsBuilder, NavigationGraphFactory.build(graph)) return relationsBuilder.build() @@ -207,7 +205,7 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp val callActivitiesBuilder = TypeSpec.objectBuilder("CallActivities") .addKdoc( "Call activities grouped by element. Each nested object exposes the called `PROCESS_ID` plus " + - "the variable mappings passed into (`Inputs`) and returned from (`Outputs`) the called process.\n" + "the variable mappings passed into (`Inputs`) and returned from (`Outputs`) the called process.\n", ) modelApi.model.callActivities .sortedBy { it.getRawName() } @@ -221,7 +219,7 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp objectBuilder.addProperty( PropertySpec.builder("PROCESS_ID", processIdClass) .initializer("%T(%L)", processIdClass, stringLiteral(callActivity.getValue())) - .build() + .build(), ) buildMappingsObject("Inputs", callActivity.inputMappings)?.let { objectBuilder.addType(it) } buildMappingsObject("Outputs", callActivity.outputMappings)?.let { objectBuilder.addType(it) } @@ -274,7 +272,7 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp val tasksBuilder = TypeSpec.objectBuilder("ServiceTasks") .addKdoc( "Job worker task types used in `@JobWorker(type = ServiceTasks.X)` annotations.\n" + - "Kept as `const val String` because annotation arguments must be compile-time constants." + "Kept as `const val String` because annotation arguments must be compile-time constants.", ) modelApi.model.serviceTasks.asApiConstants() .forEach { task -> tasksBuilder.addProperty(createAttribute(task)) } @@ -302,7 +300,7 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp .addKdoc( "Process variables grouped by the BPMN element that declares them.\n" + "Direction is encoded in each variable's wrapper type: `VariableName.Input`, `VariableName.Output`, or `VariableName.InOut` when the variable is both read and written by the same element.\n" + - "Consumer APIs that take a specific subtype (e.g. `fun setOutput(v: VariableName.Output)`) get compile-time direction enforcement." + "Consumer APIs that take a specific subtype (e.g. `fun setOutput(v: VariableName.Output)`) get compile-time direction enforcement.", ) val nodesWithVariables = modelApi.model.allFlowNodes .filter { it.variables.isNotEmpty() } @@ -388,24 +386,18 @@ internal class KotlinProcessApiBuilder : CodeGenerationAdapter.AbstractProcessAp } } - private fun createAttribute(variable: VariableMapping): PropertySpec { - return PropertySpec.builder(variable.getName(), String::class) - .addModifiers(KModifier.CONST) - .initializer("%L", stringLiteral(variable.getValue())) - .build() - } + private fun createAttribute(variable: VariableMapping): PropertySpec = PropertySpec.builder(variable.getName(), String::class) + .addModifiers(KModifier.CONST) + .initializer("%L", stringLiteral(variable.getValue())) + .build() - private fun createTypedAttribute(variable: VariableMapping, wrapperClass: ClassName): PropertySpec { - return PropertySpec.builder(variable.getName(), wrapperClass) - .initializer("%T(%L)", wrapperClass, stringLiteral(variable.getValue())) - .build() - } + private fun createTypedAttribute(variable: VariableMapping, wrapperClass: ClassName): PropertySpec = PropertySpec.builder(variable.getName(), wrapperClass) + .initializer("%T(%L)", wrapperClass, stringLiteral(variable.getValue())) + .build() - private fun stringLiteral(value: String): CodeBlock { - return if (value.contains("\${")) { - CodeBlock.of("\$\$\"\"\"%L\"\"\"", value) - } else { - CodeBlock.of("%S", value) - } + private fun stringLiteral(value: String): CodeBlock = if (value.contains("\${")) { + CodeBlock.of("\$\$\"\"\"%L\"\"\"", value) + } else { + CodeBlock.of("%S", value) } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/VariableNameSubtype.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/VariableNameSubtype.kt index bacc88f0..867255af 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/VariableNameSubtype.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/VariableNameSubtype.kt @@ -10,7 +10,8 @@ import io.miragon.bpmn.domain.shared.VariableDirection internal enum class VariableNameSubtype(val simpleName: String) { INPUT("Input"), OUTPUT("Output"), - IN_OUT("InOut"); + IN_OUT("InOut"), + ; companion object { fun chooseFor(directions: Set): VariableNameSubtype { diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactory.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactory.kt index e18dac43..38264155 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactory.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactory.kt @@ -71,13 +71,11 @@ object NavigationGraphFactory { node: FlowNodeWithId, childrenByParent: Map>, graph: ProcessGraph, - ): NavigationGraph? { - return if (node.definition is FlowNodeDefinition.Activity.SubProcess) { - buildScope(parentId = node.id, childrenByParent = childrenByParent, graph = graph) - .takeIf { it.nodes.isNotEmpty() } - } else { - null - } + ): NavigationGraph? = if (node.definition is FlowNodeDefinition.Activity.SubProcess) { + buildScope(parentId = node.id, childrenByParent = childrenByParent, graph = graph) + .takeIf { it.nodes.isNotEmpty() } + } else { + null } /** @@ -89,20 +87,14 @@ object NavigationGraphFactory { node: FlowNodeDefinition, scopeNames: Map, graph: ProcessGraph, - ): List { - return (graph.followingElementsOf(node) + graph.attachedElementsOf(node)) - .distinct() - .mapNotNull { targetId -> scopeNames[targetId] } - .distinctBy { it.objectName } - .sortedBy { it.propertyName } - .map { NavigationEdge(propertyName = it.propertyName, objectName = it.objectName) } - } + ): List = (graph.followingElementsOf(node) + graph.attachedElementsOf(node)) + .distinct() + .mapNotNull { targetId -> scopeNames[targetId] } + .distinctBy { it.objectName } + .sortedBy { it.propertyName } + .map { NavigationEdge(propertyName = it.propertyName, objectName = it.objectName) } - private fun FlowNodeDefinition.isStartEvent(): Boolean { - return this is FlowNodeDefinition.Event && shape == EventShape.START_EVENT - } + private fun FlowNodeDefinition.isStartEvent(): Boolean = this is FlowNodeDefinition.Event && shape == EventShape.START_EVENT - private fun FlowNodeDefinition.calledProcessId(): String? { - return (this as? FlowNodeDefinition.Activity.CallActivity)?.definition?.getValue()?.ifBlank { null } - } + private fun FlowNodeDefinition.calledProcessId(): String? = (this as? FlowNodeDefinition.Activity.CallActivity)?.definition?.getValue()?.ifBlank { null } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationNaming.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationNaming.kt index 9b4cdb6c..70e54ecd 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationNaming.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationNaming.kt @@ -23,14 +23,10 @@ internal object NavigationNaming { /** * Assigns [Names] to every node in a scope, keyed by element id. */ - fun assignScope(nodes: List): Map { - return nodes.associate { node -> - val objectName = node.definition.getRawName().toCamelCase() - node.id to Names(objectName, decapitalize(objectName)) - } + fun assignScope(nodes: List): Map = nodes.associate { node -> + val objectName = node.definition.getRawName().toCamelCase() + node.id to Names(objectName, decapitalize(objectName)) } - private fun decapitalize(name: String): String { - return name.replaceFirstChar { it.lowercaseChar() } - } + private fun decapitalize(name: String): String = name.replaceFirstChar { it.lowercaseChar() } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapter.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapter.kt index f9122395..30dd4419 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapter.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapter.kt @@ -10,7 +10,7 @@ import io.miragon.bpmn.domain.ProcessModel import io.miragon.bpmn.domain.shared.ProcessEngine internal class ExtractBpmnAdapter( - private val dialects: Map = ExtractBpmnAdapter.dialects + private val dialects: Map = ExtractBpmnAdapter.dialects, ) : ExtractBpmnPort { override fun extract( @@ -24,12 +24,12 @@ internal class ExtractBpmnAdapter( } catch (ex: IllegalStateException) { throw IllegalStateException( "Failed to extract file: ${bpmnFile.fileName}. Please check its a valid file for $engine", - ex + ex, ) } catch (ex: IllegalArgumentException) { throw IllegalStateException( "Failed to extract file: ${bpmnFile.fileName}. Please check its a valid file for $engine", - ex + ex, ) } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnDefinitionsReader.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnDefinitionsReader.kt index 5304e247..8060d2a8 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnDefinitionsReader.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnDefinitionsReader.kt @@ -61,29 +61,25 @@ internal object BpmnDefinitionsReader { * message element rather than on the events referencing it. It is the only engine-specific part of a * root element, which is why it arrives as a function instead of pulling the whole dialect in here. */ - fun ModelInstance.readRootElements(correlationKeyOf: (Message) -> String?): RootElements { - return RootElements( - messages = registryOf(Message::class.java) { - RootElementDefinition.Message(id = it.id ?: it.name, name = it.name, correlationKey = correlationKeyOf(it)) - }, - signals = registryOf(Signal::class.java) { - RootElementDefinition.Signal(id = it.id ?: it.name, name = it.name) - }, - errors = registryOf(Error::class.java) { - RootElementDefinition.Error(id = it.id ?: it.name, name = it.name, code = it.errorCode) - }, - escalations = registryOf(Escalation::class.java) { - RootElementDefinition.Escalation(id = it.id ?: it.name, name = it.name, code = it.escalationCode) - }, - ) - } + fun ModelInstance.readRootElements(correlationKeyOf: (Message) -> String?): RootElements = RootElements( + messages = registryOf(Message::class.java) { + RootElementDefinition.Message(id = it.id ?: it.name, name = it.name, correlationKey = correlationKeyOf(it)) + }, + signals = registryOf(Signal::class.java) { + RootElementDefinition.Signal(id = it.id ?: it.name, name = it.name) + }, + errors = registryOf(Error::class.java) { + RootElementDefinition.Error(id = it.id ?: it.name, name = it.name, code = it.errorCode) + }, + escalations = registryOf(Escalation::class.java) { + RootElementDefinition.Escalation(id = it.id ?: it.name, name = it.name, code = it.escalationCode) + }, + ) private fun ModelInstance.registryOf( type: Class, toDefinition: (E) -> D, - ): List { - return getModelElementsByType(type).map(toDefinition).distinctBy { it.id } - } + ): List = getModelElementsByType(type).map(toDefinition).distinctBy { it.id } fun String.normalizeWhitespace(): String = this.replace(Regex("\\s+"), " ").trim() } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnStructureReader.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnStructureReader.kt index 4c543e91..d58740d6 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnStructureReader.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/bpmn/BpmnStructureReader.kt @@ -94,19 +94,22 @@ internal class BpmnStructureReader( */ fun read(): FlowScope = readScope(model.findProcess().flowElements) - private fun readScope(elements: Collection): FlowScope { - return FlowScope( - flowNodes = elements.filterIsInstance().map { it.toDefinition() }, - sequenceFlows = elements.filterIsInstance().mapNotNull { it.toDefinition() }, - ) - } + private fun readScope(elements: Collection): FlowScope = FlowScope( + flowNodes = elements.filterIsInstance().map { it.toDefinition() }, + sequenceFlows = elements.filterIsInstance().mapNotNull { it.toDefinition() }, + ) private fun FlowNode.toDefinition(): FlowNodeDefinition = when (this) { is SubProcess -> toSubProcess() + is CallActivity -> toCallActivity() + is Gateway -> toGateway() + is CatchEvent, is org.camunda.bpm.model.bpmn.instance.ThrowEvent -> toEvent() + is Task -> toTask() + else -> FlowNodeDefinition.Unknown( id = id, displayName = displayName(), @@ -139,75 +142,67 @@ internal class BpmnStructureReader( ) } - private fun CallActivity.toCallActivity(): FlowNodeDefinition.Activity.CallActivity { - return FlowNodeDefinition.Activity.CallActivity( - id = id, - definition = dialect.callActivityOf(this), - displayName = displayName(), - incoming = incomingFlowIds(), - outgoing = outgoingFlowIds(), - multiInstance = multiInstance(), - ioMapping = dialect.ioMappingOf(this), - boundaryEventRefs = boundaryEventRefs(), - isForCompensation = isForCompensation, - defaultFlow = defaultFlowId(), - variables = dialect.variablesOf(this), - extensions = extensionReader.extensionsOf(id), - engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), - ) - } - - private fun Task.toTask(): FlowNodeDefinition.Activity.Task { - return FlowNodeDefinition.Activity.Task( - id = id, - kind = taskKind(), - displayName = displayName(), - incoming = incomingFlowIds(), - outgoing = outgoingFlowIds(), - implementation = dialect.implementationOf(this), - message = taskMessage(), - multiInstance = multiInstance(), - ioMapping = dialect.ioMappingOf(this), - boundaryEventRefs = boundaryEventRefs(), - isForCompensation = isForCompensation, - defaultFlow = defaultFlowId(), - variables = dialect.variablesOf(this), - extensions = extensionReader.extensionsOf(id), - engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), - ) - } - - private fun Gateway.toGateway(): FlowNodeDefinition.Gateway { - return FlowNodeDefinition.Gateway( - id = id, - kind = gatewayKind(), - displayName = displayName(), - incoming = incomingFlowIds(), - outgoing = outgoingFlowIds(), - defaultFlow = defaultFlowId(), - variables = dialect.variablesOf(this), - extensions = extensionReader.extensionsOf(id), - engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), - ) - } - - private fun FlowNode.toEvent(): FlowNodeDefinition.Event { - return FlowNodeDefinition.Event( - id = id, - shape = eventShape(), - displayName = displayName(), - incoming = incomingFlowIds(), - outgoing = outgoingFlowIds(), - eventDefinitions = eventDefinitions(), - attachedToRef = (this as? BoundaryEvent)?.attachedTo?.id, - interrupting = interrupting(), - implementation = dialect.implementationOf(this), - ioMapping = dialect.ioMappingOf(this), - variables = dialect.variablesOf(this), - extensions = extensionReader.extensionsOf(id), - engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), - ) - } + private fun CallActivity.toCallActivity(): FlowNodeDefinition.Activity.CallActivity = FlowNodeDefinition.Activity.CallActivity( + id = id, + definition = dialect.callActivityOf(this), + displayName = displayName(), + incoming = incomingFlowIds(), + outgoing = outgoingFlowIds(), + multiInstance = multiInstance(), + ioMapping = dialect.ioMappingOf(this), + boundaryEventRefs = boundaryEventRefs(), + isForCompensation = isForCompensation, + defaultFlow = defaultFlowId(), + variables = dialect.variablesOf(this), + extensions = extensionReader.extensionsOf(id), + engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), + ) + + private fun Task.toTask(): FlowNodeDefinition.Activity.Task = FlowNodeDefinition.Activity.Task( + id = id, + kind = taskKind(), + displayName = displayName(), + incoming = incomingFlowIds(), + outgoing = outgoingFlowIds(), + implementation = dialect.implementationOf(this), + message = taskMessage(), + multiInstance = multiInstance(), + ioMapping = dialect.ioMappingOf(this), + boundaryEventRefs = boundaryEventRefs(), + isForCompensation = isForCompensation, + defaultFlow = defaultFlowId(), + variables = dialect.variablesOf(this), + extensions = extensionReader.extensionsOf(id), + engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), + ) + + private fun Gateway.toGateway(): FlowNodeDefinition.Gateway = FlowNodeDefinition.Gateway( + id = id, + kind = gatewayKind(), + displayName = displayName(), + incoming = incomingFlowIds(), + outgoing = outgoingFlowIds(), + defaultFlow = defaultFlowId(), + variables = dialect.variablesOf(this), + extensions = extensionReader.extensionsOf(id), + engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), + ) + + private fun FlowNode.toEvent(): FlowNodeDefinition.Event = FlowNodeDefinition.Event( + id = id, + shape = eventShape(), + displayName = displayName(), + incoming = incomingFlowIds(), + outgoing = outgoingFlowIds(), + eventDefinitions = eventDefinitions(), + attachedToRef = (this as? BoundaryEvent)?.attachedTo?.id, + interrupting = interrupting(), + implementation = dialect.implementationOf(this), + ioMapping = dialect.ioMappingOf(this), + variables = dialect.variablesOf(this), + extensions = extensionReader.extensionsOf(id), + engineAttributes = extensionReader.foreignAttributesOf(id, dialect.fullyReadAttributesOf(this)), + ) private fun SequenceFlow.toDefinition(): SequenceFlowDefinition? { val sourceRef = source?.id ?: return null @@ -255,20 +250,17 @@ internal class BpmnStructureReader( else -> null } - private fun org.camunda.bpm.model.bpmn.instance.Message.toReference(): MessageReference { - return MessageReference( - messageRef = id ?: name, - messageName = name, - ) - } + private fun org.camunda.bpm.model.bpmn.instance.Message.toReference(): MessageReference = MessageReference( + messageRef = id ?: name, + messageName = name, + ) - private fun FlowNode.eventDefinitions(): List { - return getChildElementsByType(EventDefinition::class.java).mapNotNull { it.toInstance() } - } + private fun FlowNode.eventDefinitions(): List = getChildElementsByType(EventDefinition::class.java).mapNotNull { it.toInstance() } @Suppress("CyclomaticComplexMethod") private fun EventDefinition.toInstance(): EventDefinitionInstance? = when (this) { is TimerEventDefinition -> toTimer() + is MessageEventDefinition -> EventDefinitionInstance.Message( reference = message?.toReference() ?: MessageReference(), ) @@ -300,7 +292,9 @@ internal class BpmnStructureReader( ) is LinkEventDefinition -> EventDefinitionInstance.Link(linkName = name) + is TerminateEventDefinition -> EventDefinitionInstance.Terminate + else -> null } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaDialect.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaDialect.kt index f6e26b05..fc07f31b 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaDialect.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaDialect.kt @@ -53,6 +53,7 @@ internal class CamundaDialect(override val namespace: String) : EngineDialect { override fun fullyReadAttributesOf(node: FlowNode): Set { val resolved = when (node) { is ServiceTask -> node.attributeImplementation() + else -> node.getChildElementsByType(MessageEventDefinition::class.java) .firstNotNullOfOrNull { it.attributeImplementation() } } @@ -71,12 +72,10 @@ internal class CamundaDialect(override val namespace: String) : EngineDialect { override fun multiInstanceBindingsOf( loop: MultiInstanceLoopCharacteristics, base: MultiInstanceDefinition, - ): MultiInstanceDefinition { - return base.copy( - inputCollection = loop.attribute(CamundaModelConstants.COLLECTION_ATTRIBUTE), - inputElement = loop.attribute(CamundaModelConstants.ELEMENT_VARIABLE_ATTRIBUTE), - ) - } + ): MultiInstanceDefinition = base.copy( + inputCollection = loop.attribute(CamundaModelConstants.COLLECTION_ATTRIBUTE), + inputElement = loop.attribute(CamundaModelConstants.ELEMENT_VARIABLE_ATTRIBUTE), + ) override fun variablesOf(node: FlowNode): List { val extensions = node.findExtensionElements() @@ -117,26 +116,20 @@ internal class CamundaDialect(override val namespace: String) : EngineDialect { private data class ResolvedImplementation(val attributeName: String, val implementation: TaskImplementation) - private fun ModelElementInstance.attributeImplementation(): ResolvedImplementation? { - return implementationAttributes.firstNotNullOfOrNull { (name, build) -> - attribute(name)?.let { ResolvedImplementation(name, build(it)) } - } + private fun ModelElementInstance.attributeImplementation(): ResolvedImplementation? = implementationAttributes.firstNotNullOfOrNull { (name, build) -> + attribute(name)?.let { ResolvedImplementation(name, build(it)) } } - private fun ModelElementInstance.attribute(name: String): String? { - return getAttributeValueNs(namespace, name)?.takeIf { it.isNotBlank() } - } + private fun ModelElementInstance.attribute(name: String): String? = getAttributeValueNs(namespace, name)?.takeIf { it.isNotBlank() } - private fun FlowNode.multiInstanceVariables(): List> { - return getChildElementsByType(MultiInstanceLoopCharacteristics::class.java) - .flatMap { loop -> - listOfNotNull( - loop.attribute(CamundaModelConstants.COLLECTION_ATTRIBUTE), - loop.attribute(CamundaModelConstants.ELEMENT_VARIABLE_ATTRIBUTE), - ) - } - .map { Triple(it, VariableDirection.INPUT, it) } - } + private fun FlowNode.multiInstanceVariables(): List> = getChildElementsByType(MultiInstanceLoopCharacteristics::class.java) + .flatMap { loop -> + listOfNotNull( + loop.attribute(CamundaModelConstants.COLLECTION_ATTRIBUTE), + loop.attribute(CamundaModelConstants.ELEMENT_VARIABLE_ATTRIBUTE), + ) + } + .map { Triple(it, VariableDirection.INPUT, it) } private fun List.callActivityMappingVariables(): List> { val inElements = filterByType(BpmnModelConstants.CAMUNDA_ELEMENT_IN) @@ -158,13 +151,11 @@ internal class CamundaDialect(override val namespace: String) : EngineDialect { outputs.map { Triple(it, VariableDirection.OUTPUT, null) } } - private fun List.valuesOfProperty(propertyName: String): List { - return withAttribute(BpmnModelConstants.CAMUNDA_ATTRIBUTE_NAME to propertyName) - .mapNotNull { it.getAttribute(BpmnModelConstants.CAMUNDA_ATTRIBUTE_VALUE) } - .flatMap { it.split(",") } - .map { it.trim() } - .filter { it.isNotBlank() } - } + private fun List.valuesOfProperty(propertyName: String): List = withAttribute(BpmnModelConstants.CAMUNDA_ATTRIBUTE_NAME to propertyName) + .mapNotNull { it.getAttribute(BpmnModelConstants.CAMUNDA_ATTRIBUTE_VALUE) } + .flatMap { it.split(",") } + .map { it.trim() } + .filter { it.isNotBlank() } private fun List.toCallActivityMappings(): List { val inputs = filterByType(BpmnModelConstants.CAMUNDA_ELEMENT_IN) diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaModelConstants.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaModelConstants.kt index d0e8d597..d6f55a60 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaModelConstants.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/CamundaModelConstants.kt @@ -14,4 +14,4 @@ internal object CamundaModelConstants { const val COLLECTION_ATTRIBUTE = "collection" const val ELEMENT_VARIABLE_ATTRIBUTE = "elementVariable" -} \ No newline at end of file +} diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/EngineDialect.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/EngineDialect.kt index 27a9d1b5..be276d26 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/EngineDialect.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/EngineDialect.kt @@ -61,7 +61,7 @@ internal interface EngineDialect { */ fun multiInstanceBindingsOf( loop: MultiInstanceLoopCharacteristics, - base: MultiInstanceDefinition + base: MultiInstanceDefinition, ): MultiInstanceDefinition /** diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/ZeebeDialect.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/ZeebeDialect.kt index 94f95d95..0ea1ea6c 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/ZeebeDialect.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/dialect/ZeebeDialect.kt @@ -114,8 +114,8 @@ internal class ZeebeDialect : EngineDialect { } private fun IoMapping?.toCallActivityMappings(): List { - val inputs = this?.inputs.orEmpty().map { CallActivityDefinition.Mapping( - direction = VariableDirection.INPUT, source = it.source, target = it.target) + val inputs = this?.inputs.orEmpty().map { + CallActivityDefinition.Mapping(direction = VariableDirection.INPUT, source = it.source, target = it.target) } val outputs = this?.outputs.orEmpty().map { CallActivityDefinition.Mapping(VariableDirection.OUTPUT, source = it.source, target = it.target) } return inputs + outputs @@ -141,9 +141,7 @@ internal class ZeebeDialect : EngineDialect { outputs.map { Triple(it.removePrefix("="), VariableDirection.OUTPUT, it) } } - private fun List.attributeValues(vararg names: String): List { - return names.flatMap { name -> mapNotNull { it.domElement.getAttribute(name) } } - } + private fun List.attributeValues(vararg names: String): List = names.flatMap { name -> mapNotNull { it.domElement.getAttribute(name) } } private fun DomElement.toParameter(): IoMapping.Parameter? { val target = getAttribute(ZeebeModelConstants.ATTRIBUTE_TARGET)?.takeIf { it.isNotBlank() } ?: return null @@ -151,7 +149,5 @@ internal class ZeebeDialect : EngineDialect { return IoMapping.Parameter(target = target, source = source) } - private fun ModelElementInstance.propagateFlag(attribute: String): Boolean? { - return getAttributeValue(attribute)?.takeIf { it.isNotBlank() }?.toBooleanStrictOrNull() - } + private fun ModelElementInstance.propagateFlag(attribute: String): Boolean? = getAttributeValue(attribute)?.takeIf { it.isNotBlank() }?.toBooleanStrictOrNull() } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/CamundaXmlApi.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/CamundaXmlApi.kt index e682a621..601e7703 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/CamundaXmlApi.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/CamundaXmlApi.kt @@ -13,41 +13,23 @@ import org.camunda.bpm.model.xml.instance.ModelElementInstance */ internal object CamundaXmlApi { - fun BaseElement.findExtensionElements(): List { - return this.extensionElements?.elementsQuery?.list() ?: emptyList() - } + fun BaseElement.findExtensionElements(): List = this.extensionElements?.elementsQuery?.list() ?: emptyList() - fun BaseElement.findExtensionElementsWithType(type: String): List { - return this.findExtensionElements().filterByType(type) - } + fun BaseElement.findExtensionElementsWithType(type: String): List = this.findExtensionElements().filterByType(type) - fun BaseElement.findExtensionElement(type: String): ModelElementInstance? { - return this.findExtensionElementsWithType(type).firstOrNull() - } + fun BaseElement.findExtensionElement(type: String): ModelElementInstance? = this.findExtensionElementsWithType(type).firstOrNull() - fun List.findFirstByType(typeName: String): ModelElementInstance? { - return firstOrNull { it.elementType.typeName == typeName } - } + fun List.findFirstByType(typeName: String): ModelElementInstance? = firstOrNull { it.elementType.typeName == typeName } - fun List.filterByType(typeName: String): List { - return filter { it.elementType.typeName == typeName } - } + fun List.filterByType(typeName: String): List = filter { it.elementType.typeName == typeName } - fun List.extractAttribute(attributeName: String): List { - return mapNotNull { it.domElement.getAttribute(attributeName) } - } + fun List.extractAttribute(attributeName: String): List = mapNotNull { it.domElement.getAttribute(attributeName) } - fun ModelElementInstance.nonBlankAttribute(name: String): String? { - return getAttributeValue(name)?.takeIf { it.isNotBlank() } - } + fun ModelElementInstance.nonBlankAttribute(name: String): String? = getAttributeValue(name)?.takeIf { it.isNotBlank() } - fun ModelElementInstance.nonBlankAttributeNs(namespace: String, name: String): String? { - return getAttributeValueNs(namespace, name)?.takeIf { it.isNotBlank() } - } + fun ModelElementInstance.nonBlankAttributeNs(namespace: String, name: String): String? = getAttributeValueNs(namespace, name)?.takeIf { it.isNotBlank() } - fun List.withElementName(vararg names: String): List { - return filter { names.contains(it.localName) } - } + fun List.withElementName(vararg names: String): List = filter { names.contains(it.localName) } fun List.withAttribute(pair: Pair): List { val (attributeName, expectedValue) = pair diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/ForeignXmlReader.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/ForeignXmlReader.kt index 64639cf6..5dd9bc3c 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/ForeignXmlReader.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/ForeignXmlReader.kt @@ -55,9 +55,7 @@ internal class ForeignXmlReader( * would duplicate it. Matched on namespace *and* local name, so an identically named element from * another engine is unaffected. */ - private fun Element.isFullyReadByTheDialect(): Boolean { - return namespaceURI == engineNamespace && localNameOf() in fullyReadExtensions - } + private fun Element.isFullyReadByTheDialect(): Boolean = namespaceURI == engineNamespace && localNameOf() in fullyReadExtensions private fun Element.toExtension(): EngineExtension { val children = childElements() diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/SecureBpmnParser.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/SecureBpmnParser.kt index bd8c4b65..9408923e 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/SecureBpmnParser.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/engine/xml/SecureBpmnParser.kt @@ -1,13 +1,13 @@ package io.miragon.bpmn.adapter.outbound.engine.xml -import java.io.InputStream -import javax.xml.parsers.SAXParserFactory import org.camunda.bpm.model.bpmn.Bpmn import org.camunda.bpm.model.bpmn.BpmnModelInstance import org.xml.sax.Attributes import org.xml.sax.SAXException import org.xml.sax.SAXParseException import org.xml.sax.helpers.DefaultHandler +import java.io.InputStream +import javax.xml.parsers.SAXParserFactory /** * Camunda's Bpmn.readModelFromStream does not disable external entity resolution, making it @@ -31,12 +31,15 @@ internal object SecureBpmnParser { private fun rejectDoctypeDeclaration(stream: InputStream) { try { - saxFactory.newSAXParser().parse(stream, object : DefaultHandler() { - override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) { - // Abort as soon as we reach the first element — no DOCTYPE was encountered - throw EarlyAbortException() - } - }) + saxFactory.newSAXParser().parse( + stream, + object : DefaultHandler() { + override fun startElement(uri: String, localName: String, qName: String, attributes: Attributes) { + // Abort as soon as we reach the first element — no DOCTYPE was encountered + throw EarlyAbortException() + } + }, + ) } catch (_: EarlyAbortException) { return // clean exit — no DOCTYPE found } catch (e: SAXParseException) { diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/BpmnFileLoader.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/BpmnFileLoader.kt index a106ebf4..fe6911ca 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/BpmnFileLoader.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/BpmnFileLoader.kt @@ -47,9 +47,7 @@ internal class BpmnFileLoader : LoadBpmnFilesPort { * sorting is not a total order. Segments are joined with `/` so the key is identical across * operating systems, and plain [String] ordering keeps it locale-independent. */ - private fun relativeSortKey(searchDir: Path, file: Path): String { - return searchDir.relativize(file).joinToString("/") { it.toString() } - } + private fun relativeSortKey(searchDir: Path, file: Path): String = searchDir.relativize(file).joinToString("/") { it.toString() } private fun createMatcher(pattern: String): PathMatcher { val fs = FileSystems.getDefault() @@ -61,7 +59,6 @@ internal class BpmnFileLoader : LoadBpmnFilesPort { } private fun resolvePattern(basePath: Path, pattern: String): Pair { - if (!pattern.contains('/')) return basePath to pattern val segments = pattern.split('/') @@ -91,7 +88,7 @@ internal class BpmnFileLoader : LoadBpmnFilesPort { private data class WildcardCheckResult( val position: Int, - val isPresent: Boolean + val isPresent: Boolean, ) { fun hasNoWildcard() = !isPresent } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/ProcessApiFileSaver.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/ProcessApiFileSaver.kt index 1370680d..4b00ec38 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/ProcessApiFileSaver.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/filesystem/ProcessApiFileSaver.kt @@ -11,7 +11,7 @@ internal class ProcessApiFileSaver : SaveProcessApiPort { override fun writeFiles( generatedFiles: List, - outputFolderPath: String + outputFolderPath: String, ) { val outputFolder = File(outputFolderPath) if (!outputFolder.exists()) { diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/BpmnJsonMapper.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/BpmnJsonMapper.kt index 74ce857c..cdb7d447 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/BpmnJsonMapper.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/BpmnJsonMapper.kt @@ -40,41 +40,33 @@ import kotlinx.serialization.json.JsonPrimitive @Suppress("TooManyFunctions") internal class BpmnJsonMapper { - fun toJson(model: ProcessModel): ProcessModelJson { - return ProcessModelJson( - process = ProcessJson( - id = model.processId, - name = model.processName, - isExecutable = model.isExecutable, - engine = model.detectedEngine?.name, - flowNodes = model.flowNodes.toJson(model.sequenceFlows), - sequenceFlows = model.sequenceFlows.map { it.toJson() }, - ), - definitions = model.definitions.toJson(), - variants = model.variants.map { it.toJson() }.takeIf { it.isNotEmpty() }, - ) - } - - private fun ProcessModel.Variant.toJson(): VariantJson { - return VariantJson( - name = variantName, - flowNodes = flowNodes.toJson(sequenceFlows), - sequenceFlows = sequenceFlows.map { it.toJson() }, - ) - } - - private fun RootElements.toJson(): DefinitionsJson { - return DefinitionsJson( - messages = messages.mapNotNull { it.toJson() }.sortedBy { it.id }, - signals = signals.mapNotNull { it.toJson() }.sortedBy { it.id }, - errors = errors.mapNotNull { it.toJson() }.sortedBy { it.id }, - escalations = escalations.mapNotNull { it.toJson() }.sortedBy { it.id }, - ) - } - - private fun List.toJson(sequenceFlows: List): List { - return FlowNodeSorter.sort(this, sequenceFlows).map { it.toJson() } - } + fun toJson(model: ProcessModel): ProcessModelJson = ProcessModelJson( + process = ProcessJson( + id = model.processId, + name = model.processName, + isExecutable = model.isExecutable, + engine = model.detectedEngine?.name, + flowNodes = model.flowNodes.toJson(model.sequenceFlows), + sequenceFlows = model.sequenceFlows.map { it.toJson() }, + ), + definitions = model.definitions.toJson(), + variants = model.variants.map { it.toJson() }.takeIf { it.isNotEmpty() }, + ) + + private fun ProcessModel.Variant.toJson(): VariantJson = VariantJson( + name = variantName, + flowNodes = flowNodes.toJson(sequenceFlows), + sequenceFlows = sequenceFlows.map { it.toJson() }, + ) + + private fun RootElements.toJson(): DefinitionsJson = DefinitionsJson( + messages = messages.mapNotNull { it.toJson() }.sortedBy { it.id }, + signals = signals.mapNotNull { it.toJson() }.sortedBy { it.id }, + errors = errors.mapNotNull { it.toJson() }.sortedBy { it.id }, + escalations = escalations.mapNotNull { it.toJson() }.sortedBy { it.id }, + ) + + private fun List.toJson(sequenceFlows: List): List = FlowNodeSorter.sort(this, sequenceFlows).map { it.toJson() } private fun FlowNodeDefinition.toJson(): FlowNodeJson { val activity = this as? FlowNodeDefinition.Activity @@ -156,47 +148,37 @@ internal class BpmnJsonMapper { is TaskImplementation.Expression -> ImplementationJson.Expression(expression) } - private fun MultiInstanceDefinition.toJson(): MultiInstanceJson { - return MultiInstanceJson( - sequential = sequential, - inputCollection = inputCollection, - inputElement = inputElement, - outputCollection = outputCollection, - outputElement = outputElement, - cardinality = cardinality, - completionCondition = completionCondition, - ) - } - - private fun IoMapping.toJson(): IoMappingJson { - return IoMappingJson( - inputs = inputs.map { IoMappingJson.Parameter(it.target, it.source) }, - outputs = outputs.map { IoMappingJson.Parameter(it.target, it.source) }, - ) - } - - private fun VariableDefinition.toJson(): VariableJson { - return VariableJson(name = getRawName(), direction = direction.name, expression = valueExpression) - } - - private fun EngineExtension.toJson(): ExtensionJson { - return ExtensionJson( - type = type, - attributes = attributes, - children = children.map { it.toJson() }, - body = body, - ) - } - - private fun SequenceFlowDefinition.toJson(): SequenceFlowJson { - return SequenceFlowJson( - id = id ?: "", - sourceRef = sourceRef, - targetRef = targetRef, - name = flowName, - conditionExpression = conditionExpression, - ) - } + private fun MultiInstanceDefinition.toJson(): MultiInstanceJson = MultiInstanceJson( + sequential = sequential, + inputCollection = inputCollection, + inputElement = inputElement, + outputCollection = outputCollection, + outputElement = outputElement, + cardinality = cardinality, + completionCondition = completionCondition, + ) + + private fun IoMapping.toJson(): IoMappingJson = IoMappingJson( + inputs = inputs.map { IoMappingJson.Parameter(it.target, it.source) }, + outputs = outputs.map { IoMappingJson.Parameter(it.target, it.source) }, + ) + + private fun VariableDefinition.toJson(): VariableJson = VariableJson(name = getRawName(), direction = direction.name, expression = valueExpression) + + private fun EngineExtension.toJson(): ExtensionJson = ExtensionJson( + type = type, + attributes = attributes, + children = children.map { it.toJson() }, + body = body, + ) + + private fun SequenceFlowDefinition.toJson(): SequenceFlowJson = SequenceFlowJson( + id = id ?: "", + sourceRef = sourceRef, + targetRef = targetRef, + name = flowName, + conditionExpression = conditionExpression, + ) private fun RootElementDefinition.Message.toJson(): DefinitionsJson.Message? { val name = getValue().takeIf { it.isNotEmpty() } ?: return null diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/FlowNodeSorter.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/FlowNodeSorter.kt index 7999ae4d..aa2fc5da 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/FlowNodeSorter.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/adapter/outbound/json/FlowNodeSorter.kt @@ -32,13 +32,11 @@ internal object FlowNodeSorter { val visited = mutableSetOf() val result = mutableListOf() - fun successorsOf(node: FlowNodeDefinition): List { - return node.outgoing - .mapNotNull { targetsByFlowId[it] } - .mapNotNull { nodeById[it] } - .filter { it.id !in visited } - .sortedBy { it.id ?: "" } - } + fun successorsOf(node: FlowNodeDefinition): List = node.outgoing + .mapNotNull { targetsByFlowId[it] } + .mapNotNull { nodeById[it] } + .filter { it.id !in visited } + .sortedBy { it.id ?: "" } fun visit(node: FlowNodeDefinition) { if (node.id in visited) return @@ -67,11 +65,7 @@ internal object FlowNodeSorter { return result } - private fun FlowNodeDefinition.isNotBoundaryEvent(): Boolean { - return (this as? FlowNodeDefinition.Event)?.shape != EventShape.BOUNDARY_EVENT - } + private fun FlowNodeDefinition.isNotBoundaryEvent(): Boolean = (this as? FlowNodeDefinition.Event)?.shape != EventShape.BOUNDARY_EVENT - private fun FlowNodeDefinition.isStartEvent(): Boolean { - return (this as? FlowNodeDefinition.Event)?.shape == EventShape.START_EVENT - } + private fun FlowNodeDefinition.isStartEvent(): Boolean = (this as? FlowNodeDefinition.Event)?.shape == EventShape.START_EVENT } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/inbound/GenerateProcessApiFromFilesystemUseCase.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/inbound/GenerateProcessApiFromFilesystemUseCase.kt index 8851e5f0..8bb36a3a 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/inbound/GenerateProcessApiFromFilesystemUseCase.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/inbound/GenerateProcessApiFromFilesystemUseCase.kt @@ -16,4 +16,4 @@ interface GenerateProcessApiFromFilesystemUseCase { val engine: ProcessEngine, val validationConfig: ValidationConfig = ValidationConfig(), ) -} \ No newline at end of file +} diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/outbound/SaveProcessApiPort.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/outbound/SaveProcessApiPort.kt index 1a9e87f6..081f75d3 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/outbound/SaveProcessApiPort.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/port/outbound/SaveProcessApiPort.kt @@ -5,6 +5,6 @@ import io.miragon.bpmn.domain.GeneratedApiFile interface SaveProcessApiPort { fun writeFiles( generatedFiles: List, - outputFolderPath: String + outputFolderPath: String, ) } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsService.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsService.kt index 38599ddf..da974209 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsService.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsService.kt @@ -9,7 +9,5 @@ class ExtractProcessModelsService( private val bpmnService: ExtractBpmnPort = ExtractBpmnAdapter(), ) : ExtractProcessModelsUseCase { - override fun extractProcessModels(command: ExtractProcessModelsUseCase.Command): List { - return command.resources.map { bpmnService.extract(it, command.engine) } - } + override fun extractProcessModels(command: ExtractProcessModelsUseCase.Command): List = command.resources.map { bpmnService.extract(it, command.engine) } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryService.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryService.kt index ff744615..60500290 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryService.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryService.kt @@ -21,7 +21,7 @@ class GenerateProcessApiInMemoryService( private val modelMergerService = ModelMergerService() override fun generateProcessApi( - command: GenerateProcessApiInMemoryUseCase.Command + command: GenerateProcessApiInMemoryUseCase.Command, ): List { val validationService = BpmnValidationService(command.validationConfig) val modelsAsFiles = toBpmnFiles(command) @@ -52,5 +52,4 @@ class GenerateProcessApiInMemoryService( content = it.bpmnXml.encodeToByteArray(), ) } - } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiService.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiService.kt index 53d40db2..1ff088f0 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiService.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiService.kt @@ -53,12 +53,10 @@ class GenerateProcessApiService( private fun filterExecutableProcesses( extractedModels: List>, - ): List> { - return extractedModels.filter { (file, model) -> - val keep = model.isExecutable - if (!keep) logger.info { "Skipping '${model.processId}' (${file.fileName}): process is marked non-executable" } - keep - } + ): List> = extractedModels.filter { (file, model) -> + val keep = model.isExecutable + if (!keep) logger.info { "Skipping '${model.processId}' (${file.fileName}): process is marked non-executable" } + keep } private fun toBpmnModelApi( @@ -70,5 +68,4 @@ class GenerateProcessApiService( packagePath = command.packagePath, targetEngine = command.engine, ) - } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/BpmnModelApi.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/BpmnModelApi.kt index fa0accc4..a93960d6 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/BpmnModelApi.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/BpmnModelApi.kt @@ -24,5 +24,4 @@ data class BpmnModelApi( } private fun String.camelCase() = replaceFirstChar { it.uppercase() } - } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/ProcessModel.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/ProcessModel.kt index 2fa168a2..3b87cd06 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/ProcessModel.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/ProcessModel.kt @@ -112,37 +112,31 @@ data class ProcessModel( /** * Every signal reference in the process. */ - fun signalUsages(): List { - return allFlowNodes - .filterIsInstance() - .flatMap { node -> - node.eventDefinitions - .filterIsInstance() - .mapNotNull { it.signalName?.let { name -> NamedEventUsage(node, name, node.shape.direction) } } - } - } + fun signalUsages(): List = allFlowNodes + .filterIsInstance() + .flatMap { node -> + node.eventDefinitions + .filterIsInstance() + .mapNotNull { it.signalName?.let { name -> NamedEventUsage(node, name, node.shape.direction) } } + } /** * Every error reference in the process, paired with the event that declares it. */ - fun errorUsages(): List> { - return allFlowNodes - .filterIsInstance() - .flatMap { node -> node.eventDefinitions.filterIsInstance().map { node to it } } - } + fun errorUsages(): List> = allFlowNodes + .filterIsInstance() + .flatMap { node -> node.eventDefinitions.filterIsInstance().map { node to it } } /** * Ids of the `bpmn:Definitions` root elements the nodes actually point at. * * A file may declare more than these — `UnreferencedRootElementRule` reports the difference. */ - fun referencedDefinitionIds(): Set { - return allFlowNodes.flatMapTo(mutableSetOf()) { node -> - when (node) { - is FlowNodeDefinition.Event -> node.eventDefinitions.mapNotNull { it.referencedId() } - is FlowNodeDefinition.Activity.Task -> listOfNotNull(node.message?.messageRef) - else -> emptyList() - } + fun referencedDefinitionIds(): Set = allFlowNodes.flatMapTo(mutableSetOf()) { node -> + when (node) { + is FlowNodeDefinition.Event -> node.eventDefinitions.mapNotNull { it.referencedId() } + is FlowNodeDefinition.Activity.Task -> listOfNotNull(node.message?.messageRef) + else -> emptyList() } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/service/ModelMergerService.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/service/ModelMergerService.kt index cd50eaaf..adde6fc5 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/service/ModelMergerService.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/service/ModelMergerService.kt @@ -50,7 +50,7 @@ class ModelMergerService { private fun requireVariantNames(processId: String, models: List) { require(models.none { it.variantName.isNullOrBlank() }) { "Multiple BPMN files share process ID '$processId' but not all define a variantName. " + - "Add a variantName extension property to each process." + "Add a variantName extension property to each process." } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/CallActivityDefinition.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/CallActivityDefinition.kt index 1ef0302d..07c967dd 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/CallActivityDefinition.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/CallActivityDefinition.kt @@ -21,6 +21,7 @@ data class CallActivityDefinition( val inputMappings get() = mappings.filter { it.direction == VariableDirection.INPUT } val outputMappings get() = mappings.filter { it.direction == VariableDirection.OUTPUT } + /** * One variable passed into or out of the called process (`camunda:in` / `camunda:out`, * `zeebe:ioMapping`). diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/FlowNodeDefinition.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/FlowNodeDefinition.kt index ad17e366..de44b6a0 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/FlowNodeDefinition.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/FlowNodeDefinition.kt @@ -44,9 +44,7 @@ sealed interface FlowNodeDefinition : VariableMapping { override val extensions: List = emptyList(), override val engineAttributes: Map = emptyMap(), ) : FlowNodeDefinition { - override fun mergedWith(others: List): FlowNodeDefinition { - return copy(variables = mergeVariables(this, others)) - } + override fun mergedWith(others: List): FlowNodeDefinition = copy(variables = mergeVariables(this, others)) } /** @@ -72,9 +70,7 @@ sealed interface FlowNodeDefinition : VariableMapping { override val extensions: List = emptyList(), override val engineAttributes: Map = emptyMap(), ) : FlowNodeDefinition { - override fun mergedWith(others: List): FlowNodeDefinition { - return copy(variables = mergeVariables(this, others)) - } + override fun mergedWith(others: List): FlowNodeDefinition = copy(variables = mergeVariables(this, others)) } /** @@ -110,12 +106,10 @@ sealed interface FlowNodeDefinition : VariableMapping { override val extensions: List = emptyList(), override val engineAttributes: Map = emptyMap(), ) : Activity { - override fun mergedWith(others: List): FlowNodeDefinition { - return copy( - variables = mergeVariables(this, others), - boundaryEventRefs = mergeBoundaryEventRefs(this, others), - ) - } + override fun mergedWith(others: List): FlowNodeDefinition = copy( + variables = mergeVariables(this, others), + boundaryEventRefs = mergeBoundaryEventRefs(this, others), + ) } /** @@ -139,12 +133,10 @@ sealed interface FlowNodeDefinition : VariableMapping { override val extensions: List = emptyList(), override val engineAttributes: Map = emptyMap(), ) : Activity { - override fun mergedWith(others: List): FlowNodeDefinition { - return copy( - variables = mergeVariables(this, others), - boundaryEventRefs = mergeBoundaryEventRefs(this, others), - ) - } + override fun mergedWith(others: List): FlowNodeDefinition = copy( + variables = mergeVariables(this, others), + boundaryEventRefs = mergeBoundaryEventRefs(this, others), + ) } data class CallActivity( @@ -162,12 +154,10 @@ sealed interface FlowNodeDefinition : VariableMapping { override val extensions: List = emptyList(), override val engineAttributes: Map = emptyMap(), ) : Activity { - override fun mergedWith(others: List): FlowNodeDefinition { - return copy( - variables = mergeVariables(this, others), - boundaryEventRefs = mergeBoundaryEventRefs(this, others), - ) - } + override fun mergedWith(others: List): FlowNodeDefinition = copy( + variables = mergeVariables(this, others), + boundaryEventRefs = mergeBoundaryEventRefs(this, others), + ) } } @@ -183,9 +173,7 @@ sealed interface FlowNodeDefinition : VariableMapping { override val extensions: List = emptyList(), override val engineAttributes: Map = emptyMap(), ) : FlowNodeDefinition { - override fun mergedWith(others: List): FlowNodeDefinition { - return copy(variables = mergeVariables(this, others)) - } + override fun mergedWith(others: List): FlowNodeDefinition = copy(variables = mergeVariables(this, others)) } companion object { @@ -193,9 +181,7 @@ sealed interface FlowNodeDefinition : VariableMapping { private fun mergeVariables( node: FlowNodeDefinition, others: List, - ): List { - return (node.variables + others.flatMap { it.variables }).distinct() - } + ): List = (node.variables + others.flatMap { it.variables }).distinct() private fun mergeBoundaryEventRefs( node: Activity, diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/OutputLanguage.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/OutputLanguage.kt index 61871ec3..6abeacae 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/OutputLanguage.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/OutputLanguage.kt @@ -5,5 +5,5 @@ package io.miragon.bpmn.domain.shared */ enum class OutputLanguage { KOTLIN, - JAVA -} \ No newline at end of file + JAVA, +} diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessEngine.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessEngine.kt index 839f960f..f38b04f0 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessEngine.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessEngine.kt @@ -6,5 +6,5 @@ package io.miragon.bpmn.domain.shared enum class ProcessEngine { ZEEBE, CAMUNDA_7, - OPERATON -} \ No newline at end of file + OPERATON, +} diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessGraph.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessGraph.kt index 2fe53c82..38ab9c20 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessGraph.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/ProcessGraph.kt @@ -34,49 +34,37 @@ class ProcessGraph( /** * Ids of the flow nodes that precede [node], resolved through its incoming sequence flows. */ - fun previousElementsOf(node: FlowNodeDefinition): List { - return node.incoming.mapNotNull { flowById[it]?.sourceRef } - } + fun previousElementsOf(node: FlowNodeDefinition): List = node.incoming.mapNotNull { flowById[it]?.sourceRef } /** * Ids of the flow nodes that follow [node], resolved through its outgoing sequence flows. */ - fun followingElementsOf(node: FlowNodeDefinition): List { - return node.outgoing.mapNotNull { flowById[it]?.targetRef } - } + fun followingElementsOf(node: FlowNodeDefinition): List = node.outgoing.mapNotNull { flowById[it]?.targetRef } /** * Boundary events attached to [node]; empty for anything that is not an activity. */ - fun attachedElementsOf(node: FlowNodeDefinition): List { - return (node as? FlowNodeDefinition.Activity)?.boundaryEventRefs ?: emptyList() - } + fun attachedElementsOf(node: FlowNodeDefinition): List = (node as? FlowNodeDefinition.Activity)?.boundaryEventRefs ?: emptyList() - private fun flatten(nodes: List): List { - return nodes.flatMap { node -> - if (node is FlowNodeDefinition.Activity.SubProcess) { - listOf(node) + flatten(node.flowNodes) - } else { - listOf(node) - } + private fun flatten(nodes: List): List = nodes.flatMap { node -> + if (node is FlowNodeDefinition.Activity.SubProcess) { + listOf(node) + flatten(node.flowNodes) + } else { + listOf(node) } } - private fun nestedFlows(nodes: List): List { - return nodes.filterIsInstance() - .flatMap { it.sequenceFlows + nestedFlows(it.flowNodes) } - } + private fun nestedFlows(nodes: List): List = nodes.filterIsInstance() + .flatMap { it.sequenceFlows + nestedFlows(it.flowNodes) } private fun buildParentIndex( nodes: List, parentId: String?, - ): Map { - return buildMap { - nodes.forEach { node -> - if (parentId != null && node.id != null) put(node.id!!, parentId) - if (node is FlowNodeDefinition.Activity.SubProcess) { - putAll(buildParentIndex(node.flowNodes, node.id)) - } + ): Map = buildMap { + nodes.forEach { node -> + if (parentId != null && node.id != null) put(node.id!!, parentId) + if (node is FlowNodeDefinition.Activity.SubProcess) { + putAll(buildParentIndex(node.flowNodes, node.id)) } } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElementDefinition.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElementDefinition.kt index 76456452..6aa5ea70 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElementDefinition.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElementDefinition.kt @@ -24,7 +24,8 @@ sealed interface RootElementDefinition { override val id: String?, private val name: String?, val correlationKey: String? = null, - ) : RootElementDefinition, VariableMapping { + ) : RootElementDefinition, + VariableMapping { override fun getName() = name?.toUpperSnakeCase() ?: "" override fun getValue() = name ?: "" override fun getRawName() = name ?: "" @@ -40,7 +41,8 @@ sealed interface RootElementDefinition { data class Signal( override val id: String?, private val name: String?, - ) : RootElementDefinition, VariableMapping { + ) : RootElementDefinition, + VariableMapping { override fun getName() = name?.toUpperSnakeCase() ?: "" override fun getValue() = name ?: "" override fun getRawName() = name ?: "" @@ -54,7 +56,8 @@ sealed interface RootElementDefinition { override val id: String?, private val name: String?, private val code: String?, - ) : RootElementDefinition, VariableMapping> { + ) : RootElementDefinition, + VariableMapping> { override fun getName() = name?.toUpperSnakeCase() ?: "" override fun getValue() = (name ?: "") to (code ?: "") override fun getRawName() = name ?: "" @@ -67,7 +70,8 @@ sealed interface RootElementDefinition { override val id: String?, private val name: String?, private val code: String?, - ) : RootElementDefinition, VariableMapping> { + ) : RootElementDefinition, + VariableMapping> { override fun getName() = name?.toUpperSnakeCase() ?: "" override fun getValue() = (name ?: "") to (code ?: "") override fun getRawName() = name ?: "" diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElements.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElements.kt index 66eecd42..dfb61f66 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElements.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/shared/RootElements.kt @@ -33,16 +33,12 @@ data class RootElements( /** * Sorted by name, so generated output is a function of the model rather than of read order. */ - fun sorted(): RootElements { - return RootElements( - messages = messages.sortedBy { it.getRawName() }, - signals = signals.sortedBy { it.getRawName() }, - errors = errors.sortedBy { it.getRawName() }, - escalations = escalations.sortedBy { it.getRawName() }, - ) - } + fun sorted(): RootElements = RootElements( + messages = messages.sortedBy { it.getRawName() }, + signals = signals.sortedBy { it.getRawName() }, + errors = errors.sortedBy { it.getRawName() }, + escalations = escalations.sortedBy { it.getRawName() }, + ) - private fun List.distinctById(): List where T : VariableMapping<*>, T : RootElementDefinition { - return filter { it.getRawName().isNotEmpty() }.distinctBy { it.id ?: it.getRawName() } - } + private fun List.distinctById(): List where T : VariableMapping<*>, T : RootElementDefinition = filter { it.getRawName().isNotEmpty() }.distinctBy { it.id ?: it.getRawName() } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/utils/StringUtils.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/utils/StringUtils.kt index 91dc6782..0a44e405 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/utils/StringUtils.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/utils/StringUtils.kt @@ -12,16 +12,14 @@ object StringUtils { * @sample toUpperSnakeCase will convert #{sendMailDelegate} to SEND_MAIL_DELEGATE * @sample toUpperSnakeCase will convert io.camunda:http-json:1 to IO_CAMUNDA_HTTP_JSON_1 */ - fun String.toUpperSnakeCase(): String { - return this - .replace(Regex("[#\${}]"), "") // Strip expression language syntax - .replace(Regex("(?<=[a-zA-Z])(?=[0-9])"), "_") // Boundary between a letter and a digit - .replace(Regex("(?<=[0-9])(?=[a-zA-Z])"), "_") // Boundary between a digit and a letter - .replace(Regex("(?<=[a-z])(?=[A-Z])"), "_") // camelCase word boundary - .replace(Regex("[^A-Za-z0-9]+"), "_") // Collapse runs of non-identifier chars (e.g. . - :) into a single _ - .let { if (it.firstOrNull()?.isDigit() == true) "_$it" else it } // Identifiers must not start with a digit - .uppercase() - } + fun String.toUpperSnakeCase(): String = this + .replace(Regex("[#\${}]"), "") // Strip expression language syntax + .replace(Regex("(?<=[a-zA-Z])(?=[0-9])"), "_") // Boundary between a letter and a digit + .replace(Regex("(?<=[0-9])(?=[a-zA-Z])"), "_") // Boundary between a digit and a letter + .replace(Regex("(?<=[a-z])(?=[A-Z])"), "_") // camelCase word boundary + .replace(Regex("[^A-Za-z0-9]+"), "_") // Collapse runs of non-identifier chars (e.g. . - :) into a single _ + .let { if (it.firstOrNull()?.isDigit() == true) "_$it" else it } // Identifiers must not start with a digit + .uppercase() /** * BPMN process variables are often referenced using ${variableName} or #{beanName} syntax. @@ -32,18 +30,14 @@ object StringUtils { * @sample removeExpressionSyntax("#{sendMailDelegate}") returns "sendMailDelegate" * @sample removeExpressionSyntax("normalString") returns "normalString" */ - fun String.removeExpressionSyntax(): String { - return this.replace(Regex("[#\${}]"), "") - } + fun String.removeExpressionSyntax(): String = this.replace(Regex("[#\${}]"), "") /** * Converts a string with underscores to CamelCase, capitalising the first letter of each segment. * @sample toCamelCase will convert Activity_SendMail to ActivitySendMail * @sample toCamelCase will convert StartEvent_RequestReceived to StartEventRequestReceived */ - fun String.toCamelCase(): String { - return split(Regex("[_\\-]")) - .filter { it.isNotEmpty() } - .joinToString("") { it.replaceFirstChar { c -> c.uppercaseChar() } } - } + fun String.toCamelCase(): String = split(Regex("[_\\-]")) + .filter { it.isNotEmpty() } + .joinToString("") { it.replaceFirstChar { c -> c.uppercaseChar() } } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/model/CrossModelValidationContext.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/model/CrossModelValidationContext.kt index 2d32e797..790e44d8 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/model/CrossModelValidationContext.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/model/CrossModelValidationContext.kt @@ -19,9 +19,7 @@ data class CrossModelValidationContext( /** * Returns the model with the given process id, or `null` if no such model was loaded. */ - fun findProcess(processId: String): ProcessModel? { - return byProcessId[processId] - } + fun findProcess(processId: String): ProcessModel? = byProcessId[processId] /** * Resolves a call activity's called element to the model of the called process, diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRule.kt index 4312aca7..90083791 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRule.kt @@ -18,19 +18,17 @@ class CallActivityTargetExistsRule : CrossModelValidationRule { override val id = "call-activity-target-exists" override val severity = Severity.ERROR - override fun validate(context: CrossModelValidationContext): List { - return context.models.flatMap { model -> - model.callActivities - .filter { it.hasCalledElement() && context.resolveCalledModel(it) == null } - .map { callActivity -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = callActivity.id, - processId = model.processId, - message = "Call activity '${callActivity.id}' references unknown process '${callActivity.getValue()}'.", - ) - } - } + override fun validate(context: CrossModelValidationContext): List = context.models.flatMap { model -> + model.callActivities + .filter { it.hasCalledElement() && context.resolveCalledModel(it) == null } + .map { callActivity -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = callActivity.id, + processId = model.processId, + message = "Call activity '${callActivity.id}' references unknown process '${callActivity.getValue()}'.", + ) + } } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRule.kt index 40d614a4..5e556c6a 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRule.kt @@ -22,7 +22,7 @@ class EmptyProcessRule : SingleModelValidationRule { elementId = null, processId = context.model.processId, message = "Process has no elements defined.", - ) + ), ) } return emptyList() diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRule.kt index 95168a43..e9474372 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRule.kt @@ -35,15 +35,13 @@ class EngineMismatchRule : SingleModelValidationRule { return listOfNotNull(violation) } - private fun violation(context: SingleModelValidationContext, severity: Severity, message: String): ValidationViolation { - return ValidationViolation( - ruleId = id, - severity = severity, - elementId = null, - processId = context.model.processId, - message = message, - ) - } + private fun violation(context: SingleModelValidationContext, severity: Severity, message: String): ValidationViolation = ValidationViolation( + ruleId = id, + severity = severity, + elementId = null, + processId = context.model.processId, + message = message, + ) private fun mismatchMessage(detected: ProcessEngine, selected: ProcessEngine): String { val detectedName = displayName(detected) @@ -53,11 +51,9 @@ class EngineMismatchRule : SingleModelValidationRule { "as the process engine, or provide a model built for $selectedName." } - private fun undeterminedMessage(selected: ProcessEngine): String { - return "Could not determine this model's target engine from its BPMN namespaces, " + - "so it cannot be verified against the selected engine (${displayName(selected)}). " + - "Make sure the model carries the expected engine namespace." - } + private fun undeterminedMessage(selected: ProcessEngine): String = "Could not determine this model's target engine from its BPMN namespaces, " + + "so it cannot be verified against the selected engine (${displayName(selected)}). " + + "Make sure the model carries the expected engine namespace." private fun displayName(engine: ProcessEngine): String = when (engine) { ProcessEngine.ZEEBE -> "Zeebe (Camunda 8)" diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRule.kt index 62ce24a0..0db46b90 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRule.kt @@ -13,17 +13,15 @@ class MissingCalledElementRule : SingleModelValidationRule { override val id = "missing-called-element" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.callActivities - .filter { !it.hasCalledElement() } - .map { callActivity -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = callActivity.id, - processId = context.model.processId, - message = "Call activity is missing a 'calledElement' or 'processId' attribute.", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.callActivities + .filter { !it.hasCalledElement() } + .map { callActivity -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = callActivity.id, + processId = context.model.processId, + message = "Call activity is missing a 'calledElement' or 'processId' attribute.", + ) + } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRule.kt index 59cc7c7b..a3141886 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRule.kt @@ -13,17 +13,15 @@ class MissingErrorDefinitionRule : SingleModelValidationRule { override val id = "missing-error-definition" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.errorUsages() - .filter { (_, error) -> error.errorRef != null && (error.errorName == null || error.errorCode == null) } - .map { (node, _) -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = node.id, - processId = context.model.processId, - message = "Error event definition is missing a 'name' or 'errorCode' attribute.", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.errorUsages() + .filter { (_, error) -> error.errorRef != null && (error.errorName == null || error.errorCode == null) } + .map { (node, _) -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = node.id, + processId = context.model.processId, + message = "Error event definition is missing a 'name' or 'errorCode' attribute.", + ) + } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRule.kt index c9aef89d..7bb96319 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRule.kt @@ -17,26 +17,26 @@ class MissingMessageNameRule : SingleModelValidationRule { override val id = "missing-message-name" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.allFlowNodes - .filter { it.hasNamelessMessage() } - .map { node -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = node.id, - processId = context.model.processId, - message = "Message element is missing a 'name' attribute.", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.allFlowNodes + .filter { it.hasNamelessMessage() } + .map { node -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = node.id, + processId = context.model.processId, + message = "Message element is missing a 'name' attribute.", + ) + } private fun FlowNodeDefinition.hasNamelessMessage(): Boolean = when (this) { is FlowNodeDefinition.Event -> eventDefinitions.filterIsInstance() .any { it.reference.messageRef != null && it.reference.messageName == null } + is FlowNodeDefinition.Activity.Task -> kind in messageTaskKinds && message != null && message.messageName == null + else -> false } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRule.kt index 331129b8..69809263 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRule.kt @@ -23,7 +23,7 @@ class MissingProcessIdRule : SingleModelValidationRule { elementId = null, processId = "(unknown)", message = "BPMN model is missing a process ID.", - ) + ), ) } return emptyList() diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRule.kt index 40ba4a61..c5ddf432 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRule.kt @@ -14,19 +14,17 @@ class MissingServiceTaskImplementationRule : SingleModelValidationRule { override val id = "missing-service-task-implementation" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.serviceTasks - .filter { !it.hasImplementation() } - .map { task -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = task.id, - processId = context.model.processId, - message = "Service task has no implementation. ${engineHint(context.engine)}", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.serviceTasks + .filter { !it.hasImplementation() } + .map { task -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = task.id, + processId = context.model.processId, + message = "Service task has no implementation. ${engineHint(context.engine)}", + ) + } private fun engineHint(engine: ProcessEngine): String = when (engine) { ProcessEngine.CAMUNDA_7 -> "Set camunda:topic, camunda:class, or camunda:delegateExpression." diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRule.kt index 1a442350..38511eb6 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRule.kt @@ -13,17 +13,15 @@ class MissingSignalNameRule : SingleModelValidationRule { override val id = "missing-signal-name" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.definitions.signals - .filter { !it.hasName() } - .map { - ValidationViolation( - ruleId = id, - severity = severity, - elementId = null, - processId = context.model.processId, - message = "Signal event definition is missing a 'name' attribute.", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.definitions.signals + .filter { !it.hasName() } + .map { + ValidationViolation( + ruleId = id, + severity = severity, + elementId = null, + processId = context.model.processId, + message = "Signal event definition is missing a 'name' attribute.", + ) + } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRule.kt index 19691afd..3651e026 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRule.kt @@ -13,17 +13,15 @@ class MissingTimerDefinitionRule : SingleModelValidationRule { override val id = "missing-timer-definition" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.timers - .filter { !it.hasTimerType() } - .map { timer -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = timer.id, - processId = context.model.processId, - message = "Timer event definition has no valid type (Date, Duration, or Cycle).", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.timers + .filter { !it.hasTimerType() } + .map { timer -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = timer.id, + processId = context.model.processId, + message = "Timer event definition has no valid type (Date, Duration, or Cycle).", + ) + } } diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/TimerValueSyntax.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/TimerValueSyntax.kt index ac56c908..8b85d193 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/TimerValueSyntax.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/TimerValueSyntax.kt @@ -26,9 +26,7 @@ internal object TimerValueSyntax { * A dynamic expression (FEEL or Camunda EL) whose value is only * known at runtime. */ - fun isExpression(value: String): Boolean { - return value.startsWith("=") || value.contains("\${") || value.contains("#{") - } + fun isExpression(value: String): Boolean = value.startsWith("=") || value.contains("\${") || value.contains("#{") /** * Structural cron check: 6 or 7 whitespace-separated fields @@ -44,9 +42,7 @@ internal object TimerValueSyntax { * ISO-8601 duration, e.g. `PT15M`, `P1Y2M`, `P1DT12H` * (full xsd:duration grammar). */ - fun isValidIsoDuration(value: String): Boolean { - return value.startsWith("P") && runCatching { datatypeFactory.newDuration(value) }.isSuccess - } + fun isValidIsoDuration(value: String): Boolean = value.startsWith("P") && runCatching { datatypeFactory.newDuration(value) }.isSuccess /** * ISO-8601 point in time, e.g. `2026-01-01T00:00:00Z` or diff --git a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRule.kt b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRule.kt index f896d3c1..a446dac8 100644 --- a/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRule.kt +++ b/bpmn-to-code-core/src/main/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRule.kt @@ -37,23 +37,19 @@ class UnreferencedRootElementRule : SingleModelValidationRule { elements: List, referenced: Set, kind: String, - ): List> { - return elements.filterNot { it.id in referenced }.map { kind to it } - } + ): List> = elements.filterNot { it.id in referenced }.map { kind to it } private fun violation( model: ProcessModel, kind: String, element: RootElementDefinition, - ): ValidationViolation { - return ValidationViolation( - ruleId = id, - severity = severity, - elementId = element.id, - processId = model.processId, - message = "$kind '${element.id}' is declared but no element references it. " + - "It still produces a constant in the generated API — remove it from the BPMN file " + - "if it is left over from an earlier version of the model.", - ) - } + ): ValidationViolation = ValidationViolation( + ruleId = id, + severity = severity, + elementId = element.id, + processId = model.processId, + message = "$kind '${element.id}' is declared but no element references it. " + + "It still produces a constant in the generated API — remove it from the BPMN file " + + "if it is left over from an earlier version of the model.", + ) } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPluginTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPluginTest.kt index 64d8479b..312b61dc 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPluginTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiFilesystemPluginTest.kt @@ -15,7 +15,6 @@ class CreateProcessApiFilesystemPluginTest { @Test fun `execute delegates to use case with correct command`() { - // when: execute is called with all parameters underTest.execute( baseDir = "/path/to/bpmn", @@ -36,7 +35,7 @@ class CreateProcessApiFilesystemPluginTest { packagePath = "com.example.api", outputLanguage = OutputLanguage.KOTLIN, engine = ProcessEngine.ZEEBE, - ) + ), ) } confirmVerified(useCase) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPluginTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPluginTest.kt index 741458ef..fedd463e 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPluginTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessApiInMemoryPluginTest.kt @@ -18,7 +18,6 @@ class CreateProcessApiInMemoryPluginTest { @Test fun `execute delegates to use case and returns generated files`() { - // given: multiple BpmnInput objects val firstInput = mockInput("first") val secondInput = mockInput("second") @@ -30,7 +29,7 @@ class CreateProcessApiInMemoryPluginTest { bpmnContents = listOf(firstInput, secondInput), packagePath = "com.example.api", outputLanguage = OutputLanguage.KOTLIN, - engine = ProcessEngine.ZEEBE + engine = ProcessEngine.ZEEBE, ) // then: a use case is called with correct command mapping and returns generated files @@ -43,14 +42,14 @@ class CreateProcessApiInMemoryPluginTest { bpmnContents = listOf( GenerateProcessApiInMemoryUseCase.BpmnInput( bpmnXml = "first", - processName = "first.bpmn" + processName = "first.bpmn", ), GenerateProcessApiInMemoryUseCase.BpmnInput( bpmnXml = "second", - processName = "second.bpmn" - ) - ) - ) + processName = "second.bpmn", + ), + ), + ), ) } @@ -61,7 +60,7 @@ class CreateProcessApiInMemoryPluginTest { private fun mockInput(fileName: String) = CreateProcessApiInMemoryPlugin.BpmnInput( bpmnXml = "$fileName", - processName = "$fileName.bpmn" + processName = "$fileName.bpmn", ) private fun mockApiFile(fileName: String) = GeneratedApiFile( @@ -71,5 +70,4 @@ class CreateProcessApiInMemoryPluginTest { language = OutputLanguage.KOTLIN, processId = fileName, ) - } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPluginTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPluginTest.kt index b0ffaf89..e3ce4206 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPluginTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonFilesystemPluginTest.kt @@ -14,7 +14,6 @@ class CreateProcessJsonFilesystemPluginTest { @Test fun `execute delegates to use case with correct command`() { - // when: execute is called with all parameters underTest.execute( baseDir = "/path/to/bpmn", @@ -31,7 +30,7 @@ class CreateProcessJsonFilesystemPluginTest { filePattern = "*.bpmn", outputFolderPath = "/output/folder", engine = ProcessEngine.ZEEBE, - ) + ), ) } confirmVerified(useCase) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPluginTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPluginTest.kt index 4d59049c..519c76be 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPluginTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/CreateProcessJsonInMemoryPluginTest.kt @@ -17,7 +17,6 @@ class CreateProcessJsonInMemoryPluginTest { @Test fun `execute delegates to use case and returns generated files`() { - // given: multiple BpmnInput objects val firstInput = CreateProcessJsonInMemoryPlugin.BpmnInput(bpmnXml = "first", processName = "first.bpmn") val secondInput = CreateProcessJsonInMemoryPlugin.BpmnInput(bpmnXml = "second", processName = "second.bpmn") @@ -42,7 +41,7 @@ class CreateProcessJsonInMemoryPluginTest { GenerateProcessJsonInMemoryUseCase.BpmnInput(bpmnXml = "first", processName = "first.bpmn"), GenerateProcessJsonInMemoryUseCase.BpmnInput(bpmnXml = "second", processName = "second.bpmn"), ), - ) + ), ) } assertThat(result).isEqualTo(expectedFiles) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPluginTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPluginTest.kt index 05ac1fb0..9defa831 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPluginTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/inbound/ValidateBpmnFilesystemPluginTest.kt @@ -18,7 +18,6 @@ class ValidateBpmnFilesystemPluginTest { @Test fun `execute delegates to use case with correct command`() { - // given: a config and an expected validation result val config = ValidationConfig(failOnWarning = true, disabledRules = setOf("missing-element-id")) val expectedResult = ValidationResult(emptyList()) @@ -40,7 +39,7 @@ class ValidateBpmnFilesystemPluginTest { filePattern = "*.bpmn", engine = ProcessEngine.ZEEBE, validationConfig = config, - ) + ), ) } assertThat(result).isEqualTo(expectedResult) @@ -49,7 +48,6 @@ class ValidateBpmnFilesystemPluginTest { @Test fun `execute uses default validation config when not provided`() { - // given: an expected validation result val expectedResult = ValidationResult(emptyList()) every { useCase.validateBpmn(any()) } returns expectedResult @@ -69,7 +67,7 @@ class ValidateBpmnFilesystemPluginTest { filePattern = "*.bpmn", engine = ProcessEngine.CAMUNDA_7, validationConfig = ValidationConfig(), - ) + ), ) } assertThat(result).isEqualTo(expectedResult) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapterTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapterTest.kt index c023dd4e..298b9bce 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapterTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/CodeGenerationAdapterTest.kt @@ -20,7 +20,6 @@ class CodeGenerationAdapterTest { @Test fun `generateCode delegates to the process api builder and returns its file`() { - // given: a model API and a stubbed process builder response val modelApi = testProcessModelApi() val processFile = GeneratedApiFile( @@ -43,7 +42,6 @@ class CodeGenerationAdapterTest { @Test fun `generateCode throws when output language is not supported`() { - // given: a model API with an unsupported language val modelApi = testProcessModelApi(language = OutputLanguage.JAVA) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/NestedSubProcessCompilationTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/NestedSubProcessCompilationTest.kt index e029dc36..00b7edd4 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/NestedSubProcessCompilationTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/NestedSubProcessCompilationTest.kt @@ -33,20 +33,19 @@ class NestedSubProcessCompilationTest { .isEmpty() } - private fun generate(language: OutputLanguage) = - service.generateProcessApi( - GenerateProcessApiInMemoryUseCase.Command( - bpmnContents = listOf( - GenerateProcessApiInMemoryUseCase.BpmnInput( - bpmnXml = requireNotNull(javaClass.getResource("/bpmn/nested-subprocess.bpmn")).readText(), - processName = "nested-subprocess.bpmn", - ), + private fun generate(language: OutputLanguage) = service.generateProcessApi( + GenerateProcessApiInMemoryUseCase.Command( + bpmnContents = listOf( + GenerateProcessApiInMemoryUseCase.BpmnInput( + bpmnXml = requireNotNull(javaClass.getResource("/bpmn/nested-subprocess.bpmn")).readText(), + processName = "nested-subprocess.bpmn", ), - packagePath = "de.gen", - outputLanguage = language, - engine = ProcessEngine.ZEEBE, ), - ).single() + packagePath = "de.gen", + outputLanguage = language, + engine = ProcessEngine.ZEEBE, + ), + ).single() private fun compileJava(fileName: String, source: String): List { val compiler = requireNotNull(ToolProvider.getSystemJavaCompiler()) { "JDK (not JRE) required to run this test" } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilderTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilderTest.kt index 98567723..02ba421b 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilderTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/JavaProcessApiBuilderTest.kt @@ -12,6 +12,8 @@ import io.miragon.bpmn.domain.testProcessModelApi import io.miragon.bpmn.domain.testSendNewsletterModel import io.miragon.bpmn.domain.testSubscribeNewsletterModel import io.miragon.bpmn.domain.withId +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test import java.io.File import java.net.URI import javax.tools.Diagnostic @@ -19,8 +21,6 @@ import javax.tools.DiagnosticCollector import javax.tools.JavaFileObject import javax.tools.SimpleJavaFileObject import javax.tools.ToolProvider -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test class JavaProcessApiBuilderTest { @@ -28,7 +28,6 @@ class JavaProcessApiBuilderTest { @Test fun `buildApiFile generates correct process API file`() { - // given: a BPMN model with custom service task implementations val modelApi = testProcessModelApi( packagePath = "de.emaarco.example", @@ -40,7 +39,7 @@ class JavaProcessApiBuilderTest { notifyCommunityImpl = "newsletter.notifyCommunity", extraVariables = listOf(VariableDefinition("testVariable", VariableDirection.INPUT)), ), - ) + ), ) // when: we build the process API file @@ -57,13 +56,12 @@ class JavaProcessApiBuilderTest { @Test fun `maps content of id to valid variable name format`() { - // given: a model with flow nodes that have slashes in their names val defaultModel = testSubscribeNewsletterModel() val modifiedNodes = defaultModel.flowNodes.map { it.withId(it.getName().replace("_", "-")) } val modelApi = testProcessModelApi( model = testSubscribeNewsletterModel(flowNodes = modifiedNodes), - packagePath = "de.emaarco.example" + packagePath = "de.emaarco.example", ) // when: we build the process API file @@ -76,7 +74,6 @@ class JavaProcessApiBuilderTest { @Test fun `buildApiFile generates variant-scoped Flows and Relations for merged model`() { - // given: a merged model with a single variant val send = testSendNewsletterModel(variantName = "send") val merged = ProcessModel( @@ -103,7 +100,8 @@ class JavaProcessApiBuilderTest { val diagnostics = DiagnosticCollector() val fileManager = compiler.getStandardFileManager(diagnostics, null, null) val sourceObject = object : SimpleJavaFileObject( - URI.create("string:///${fileName.replace('.', '/')}"), JavaFileObject.Kind.SOURCE + URI.create("string:///${fileName.replace('.', '/')}"), + JavaFileObject.Kind.SOURCE, ) { override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence = source } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilderTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilderTest.kt index 3b58c224..ccf9fd6f 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilderTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/builder/KotlinProcessApiBuilderTest.kt @@ -11,7 +11,6 @@ import io.miragon.bpmn.domain.shared.VariableDirection import io.miragon.bpmn.domain.testProcessModelApi import io.miragon.bpmn.domain.testSendNewsletterModel import io.miragon.bpmn.domain.testSubscribeNewsletterModel -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.jetbrains.kotlin.K1Deprecation import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -24,6 +23,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtTreeVisitorVoid import org.junit.jupiter.api.Test +import java.io.File class KotlinProcessApiBuilderTest { @@ -31,7 +31,6 @@ class KotlinProcessApiBuilderTest { @Test fun `buildApiFile generates correct process API file`() { - // given: a BPMN model with custom service task implementations val modelApi = testProcessModelApi( packagePath = "de.emaarco.example", @@ -43,7 +42,7 @@ class KotlinProcessApiBuilderTest { notifyCommunityImpl = "newsletter.notifyCommunity", extraVariables = listOf(VariableDefinition("testVariable", VariableDirection.INPUT)), ), - ) + ), ) // when: we build the process API file @@ -66,7 +65,6 @@ class KotlinProcessApiBuilderTest { @Test fun `buildApiFile generates variant-scoped Flows and Relations for merged model`() { - // given: a merged model with a single variant val send = testSendNewsletterModel(variantName = "send") val merged = ProcessModel( @@ -90,7 +88,6 @@ class KotlinProcessApiBuilderTest { @Test fun `buildApiFile emits one constant for root elements that share a name`() { - // given: two bpmn:Message root elements with the same name and their own ids — the domain keeps // both so that every messageRef resolves, but they normalise to a single constant val model = testSubscribeNewsletterModel( diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactoryTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactoryTest.kt index 5002bb0f..64aee831 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactoryTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/codegen/navigation/NavigationGraphFactoryTest.kt @@ -86,14 +86,12 @@ class NavigationGraphFactoryTest { assertThat(serviceTask.id).isEqualTo("serviceTask_incrementSubscriptionCounter") assertThat(serviceTask.elementType).isEqualTo("SERVICE_TASK") assertThat(serviceTask.objectName).isEqualTo("ServiceTaskIncrementSubscriptionCounter") - assertThat(serviceTask.name).isNull() // no displayName in the model + assertThat(serviceTask.name).isNull() // no displayName in the model // ActivityConfirmRegistration declares displayName "Confirm registration" val confirm = graph.node("subProcessConfirmation").inner!!.node("activityConfirmRegistration") assertThat(confirm.name).isEqualTo("Confirm registration") } - private fun NavigationGraph.node(propertyName: String): NavigationNode { - return nodes.single { it.propertyName == propertyName } - } + private fun NavigationGraph.node(propertyName: String): NavigationNode = nodes.single { it.propertyName == propertyName } } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ActivityFacetExtractionTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ActivityFacetExtractionTest.kt index 8f1e99a1..98ea5944 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ActivityFacetExtractionTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ActivityFacetExtractionTest.kt @@ -6,9 +6,9 @@ import io.miragon.bpmn.domain.ProcessModel import io.miragon.bpmn.domain.shared.FlowNodeDefinition import io.miragon.bpmn.domain.shared.IoMapping import io.miragon.bpmn.domain.shared.MultiInstanceDefinition -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import java.io.File /** * Guards the two activity facets the v2 model introduced: multi-instance loop characteristics @@ -24,7 +24,6 @@ class ActivityFacetExtractionTest { @Test fun `zeebe extract reads multi-instance loop characteristics`() { - // given val model = extract(ProcessModelReader(ZeebeDialect()), "c8-send-newsletter") @@ -34,7 +33,7 @@ class ActivityFacetExtractionTest { sequential = true, inputCollection = "=subscribers", inputElement = "subscriber", - ) + ), ) assertThat(model.multiInstanceOf("serviceTask_notifyAuthor")).isEqualTo( MultiInstanceDefinition( @@ -43,13 +42,12 @@ class ActivityFacetExtractionTest { inputElement = "author", outputCollection = "results", outputElement = "=result", - ) + ), ) } @Test fun `zeebe extract reads io mappings`() { - // given val model = extract(ProcessModelReader(ZeebeDialect()), "c8-send-newsletter") @@ -59,8 +57,8 @@ class ActivityFacetExtractionTest { outputs = listOf( IoMapping.Parameter(target = "subscribers", source = "=subscribers"), IoMapping.Parameter(target = "author", source = "=author"), - ) - ) + ), + ), ) assertThat(model.ioMappingOf("serviceTask_publishNewsletter")).isEqualTo( IoMapping( @@ -69,13 +67,12 @@ class ActivityFacetExtractionTest { IoMapping.Parameter(target = "url", source = "https://api.example.com/newsletter"), ), outputs = listOf(IoMapping.Parameter(target = "apiResponse", source = "=response")), - ) + ), ) } @Test fun `camunda 7 extract reads multi-instance loop characteristics`() { - // given val model = extract(ProcessModelReader(CamundaDialect(CAMUNDA_7_NAMESPACE)), "c7-send-newsletter") @@ -85,20 +82,19 @@ class ActivityFacetExtractionTest { sequential = true, inputCollection = "\${subscribers}", inputElement = "subscriber", - ) + ), ) assertThat(model.multiInstanceOf("serviceTask_notifyAuthor")).isEqualTo( MultiInstanceDefinition( sequential = false, inputCollection = "\${authors}", inputElement = "author", - ) + ), ) } @Test fun `camunda 7 extract reads io mappings`() { - // given val model = extract(ProcessModelReader(CamundaDialect(CAMUNDA_7_NAMESPACE)), "c7-send-newsletter") @@ -108,17 +104,16 @@ class ActivityFacetExtractionTest { outputs = listOf( IoMapping.Parameter(target = "subscribers", source = "\${subscribers}"), IoMapping.Parameter(target = "author", source = "\${author}"), - ) - ) + ), + ), ) assertThat(model.ioMappingOf("serviceTask_notifyAuthor")).isEqualTo( - IoMapping(inputs = listOf(IoMapping.Parameter(target = "test", source = "null"))) + IoMapping(inputs = listOf(IoMapping.Parameter(target = "test", source = "null"))), ) } @Test fun `operaton extract reads multi-instance loop characteristics`() { - // given val model = extract(ProcessModelReader(CamundaDialect(OPERATON_NAMESPACE)), "operaton-send-newsletter") @@ -128,20 +123,19 @@ class ActivityFacetExtractionTest { sequential = true, inputCollection = "subscribers", inputElement = "subscriber", - ) + ), ) assertThat(model.multiInstanceOf("serviceTask_notifyAuthor")).isEqualTo( MultiInstanceDefinition( sequential = false, inputCollection = "authors", inputElement = "author", - ) + ), ) } @Test fun `operaton extract reads io mappings`() { - // given val model = extract(ProcessModelReader(CamundaDialect(OPERATON_NAMESPACE)), "operaton-send-newsletter") @@ -151,14 +145,13 @@ class ActivityFacetExtractionTest { outputs = listOf( IoMapping.Parameter(target = "subscribers", source = "\${subscribers}"), IoMapping.Parameter(target = "author", source = "\${author}"), - ) - ) + ), + ), ) } @Test fun `an activity without loop characteristics or io mapping reports neither facet`() { - // given: the same task in all three dialects, configured with neither facet val models = listOf( extract(ProcessModelReader(ZeebeDialect()), "c8-send-newsletter"), @@ -175,7 +168,6 @@ class ActivityFacetExtractionTest { @Test fun `the same logical loop normalises identically across engines`() { - // given: the same process modelled for all three engines val models = listOf( extract(ProcessModelReader(ZeebeDialect()), "c8-send-newsletter"), @@ -201,9 +193,7 @@ class ActivityFacetExtractionTest { return reader.read(File(resourceUrl.toURI()).readBytes()) } - private fun ProcessModel.activity(id: String): FlowNodeDefinition.Activity { - return allFlowNodes.single { it.id == id } as FlowNodeDefinition.Activity - } + private fun ProcessModel.activity(id: String): FlowNodeDefinition.Activity = allFlowNodes.single { it.id == id } as FlowNodeDefinition.Activity private fun ProcessModel.multiInstanceOf(id: String): MultiInstanceDefinition? = activity(id).multiInstance diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/Camunda7ExtractionTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/Camunda7ExtractionTest.kt index 294bb708..26bdf441 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/Camunda7ExtractionTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/Camunda7ExtractionTest.kt @@ -16,9 +16,9 @@ import io.miragon.bpmn.domain.shared.TimerDefinition import io.miragon.bpmn.domain.shared.TimerType import io.miragon.bpmn.domain.shared.VariableDefinition import io.miragon.bpmn.domain.shared.VariableDirection -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import java.io.File class Camunda7ExtractionTest { @@ -26,7 +26,6 @@ class Camunda7ExtractionTest { @Test fun `extract returns valid ProcessModel`() { - // given: the Camunda 7 newsletter BPMN file from classpath val resourceUrl = requireNotNull(javaClass.getResource("/bpmn/c7-subscribe-newsletter.bpmn")) val file = File(resourceUrl.toURI()) @@ -319,10 +318,10 @@ class Camunda7ExtractionTest { val flowsById = bpmnModel.sequenceFlows.associateBy { it.id } assertThat(flowsById["Flow_1jogut0"]).isEqualTo( - SequenceFlowDefinition("Flow_1jogut0", "gateway_hasSubscribers", "serviceTask_sendToSubscriber", flowName = "Yes", isDefault = true) + SequenceFlowDefinition("Flow_1jogut0", "gateway_hasSubscribers", "serviceTask_sendToSubscriber", flowName = "Yes", isDefault = true), ) assertThat(flowsById["Flow_1gsz7wd"]).isEqualTo( - SequenceFlowDefinition("Flow_1gsz7wd", "gateway_hasSubscribers", "endEvent_noSubscribers", flowName = "No", conditionExpression = "\${subscribers.size() > 0}") + SequenceFlowDefinition("Flow_1gsz7wd", "gateway_hasSubscribers", "endEvent_noSubscribers", flowName = "No", conditionExpression = "\${subscribers.size() > 0}"), ) } @@ -351,7 +350,7 @@ class Camunda7ExtractionTest { assertThat(callActivity.propagateAllInputVariables).isTrue() assertThat(callActivity.propagateAllOutputVariables).isTrue() assertThat(callActivity.inputMappings).containsExactly( - CallActivityDefinition.Mapping(VariableDirection.INPUT, source = "orderId", target = "businessKey") + CallActivityDefinition.Mapping(VariableDirection.INPUT, source = "orderId", target = "businessKey"), ) } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapterTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapterTest.kt index befabd6f..603ea9ee 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapterTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/engine/ExtractBpmnAdapterTest.kt @@ -3,10 +3,10 @@ package io.miragon.bpmn.adapter.outbound.engine import io.miragon.bpmn.domain.BpmnResource import io.miragon.bpmn.domain.shared.ProcessEngine import io.miragon.bpmn.domain.shared.TaskImplementation -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test +import java.io.File class ExtractBpmnAdapterTest { @@ -14,7 +14,6 @@ class ExtractBpmnAdapterTest { @Test fun `extract reads the model with the dialect registered for the engine`() { - // given: the Camunda 8 newsletter model val bpmnResource = classpathResource("c8-subscribe-newsletter.bpmn") @@ -29,7 +28,6 @@ class ExtractBpmnAdapterTest { @Test fun `extract throws when no dialect is registered for the engine`() { - // given: an adapter that only knows Zeebe val zeebeOnly = ExtractBpmnAdapter(dialects = ExtractBpmnAdapter.dialects.filterKeys { it == ProcessEngine.ZEEBE }) val bpmnResource = classpathResource("c7-subscribe-newsletter.bpmn") @@ -41,7 +39,6 @@ class ExtractBpmnAdapterTest { @Test fun `extract names the offending file when the model cannot be read`() { - // given: a well-formed BPMN file that declares no process val bpmnResource = BpmnResource(fileName = "no-process.bpmn", content = DEFINITIONS_WITHOUT_PROCESS.toByteArray()) @@ -53,7 +50,6 @@ class ExtractBpmnAdapterTest { @Test fun `a malformed file is reported with its name, not as a security violation`() { - // given: a truncated BPMN file val bpmnResource = BpmnResource(fileName = "truncated.bpmn", content = "): List { - return pairs.map { (source, target) -> - SequenceFlowDefinition(id = "$source->$target", sourceRef = source, targetRef = target) - } + private fun edges(vararg pairs: Pair): List = pairs.map { (source, target) -> + SequenceFlowDefinition(id = "$source->$target", sourceRef = source, targetRef = target) } - private fun outgoingOf(id: String, flows: List): List { - return flows.filter { it.sourceRef == id }.map { it.id!! } - } + private fun outgoingOf(id: String, flows: List): List = flows.filter { it.sourceRef == id }.map { it.id!! } - private fun incomingOf(id: String, flows: List): List { - return flows.filter { it.targetRef == id }.map { it.id!! } - } + private fun incomingOf(id: String, flows: List): List = flows.filter { it.targetRef == id }.map { it.id!! } - private fun task(id: String, flows: List): FlowNodeDefinition { - return FlowNodeDefinition.Activity.Task( - id = id, - kind = TaskKind.SERVICE, - incoming = incomingOf(id, flows), - outgoing = outgoingOf(id, flows), - ) - } + private fun task(id: String, flows: List): FlowNodeDefinition = FlowNodeDefinition.Activity.Task( + id = id, + kind = TaskKind.SERVICE, + incoming = incomingOf(id, flows), + outgoing = outgoingOf(id, flows), + ) private fun event( id: String, shape: EventShape, flows: List, attachedToRef: String? = null, - ): FlowNodeDefinition { - return FlowNodeDefinition.Event( - id = id, - shape = shape, - incoming = incomingOf(id, flows), - outgoing = outgoingOf(id, flows), - attachedToRef = attachedToRef, - ) - } - - private fun gateway(id: String, kind: GatewayKind, flows: List): FlowNodeDefinition { - return FlowNodeDefinition.Gateway( - id = id, - kind = kind, - incoming = incomingOf(id, flows), - outgoing = outgoingOf(id, flows), - ) - } + ): FlowNodeDefinition = FlowNodeDefinition.Event( + id = id, + shape = shape, + incoming = incomingOf(id, flows), + outgoing = outgoingOf(id, flows), + attachedToRef = attachedToRef, + ) + + private fun gateway(id: String, kind: GatewayKind, flows: List): FlowNodeDefinition = FlowNodeDefinition.Gateway( + id = id, + kind = kind, + incoming = incomingOf(id, flows), + outgoing = outgoingOf(id, flows), + ) @Test fun `linear chain is sorted start to end`() { - // given: a linear start → task → end chain val flows = edges("Start" to "Task", "Task" to "End") val start = event("Start", EventShape.START_EVENT, flows) @@ -79,7 +66,6 @@ class FlowNodeSorterTest { @Test fun `start events are visited before other top-level nodes`() { - // given: two start events feeding the same task val flows = edges("Start_A" to "Task", "Start_B" to "Task", "Task" to "End") val startA = event("Start_A", EventShape.START_EVENT, flows) @@ -98,7 +84,6 @@ class FlowNodeSorterTest { @Test fun `boundary event appears after its parent`() { - // given: a task with an attached boundary event val flows = edges("Start" to "Task", "Task" to "End", "Boundary" to "ErrorEnd") val start = event("Start", EventShape.START_EVENT, flows) @@ -118,7 +103,6 @@ class FlowNodeSorterTest { @Test fun `subprocess is ordered in its scope and its children are sorted separately`() { - // given: a top-level scope containing a sub-process. Sub-process children are no longer inlined into // the parent scope — they live inside the sub-process node and are sorted by re-applying the sorter. val topFlows = edges("Start" to "Sub", "Sub" to "End") @@ -152,7 +136,6 @@ class FlowNodeSorterTest { @Test fun `cycles do not cause infinite loops`() { - // given: a cyclic A ↔ B loop val flows = edges("Start" to "A", "A" to "B", "B" to "A", "B" to "End") val start = event("Start", EventShape.START_EVENT, flows) @@ -170,7 +153,6 @@ class FlowNodeSorterTest { @Test fun `already sorted input is idempotent`() { - // given: nodes already in correct order val flows = edges("Start" to "Task", "Task" to "End") val start = event("Start", EventShape.START_EVENT, flows) @@ -186,7 +168,6 @@ class FlowNodeSorterTest { @Test fun `exclusive gateway branches appear after gateway`() { - // given: a gateway splitting into two branches val flows = edges( "Start" to "GW", diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonActivityFacetsTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonActivityFacetsTest.kt index 4b5e385e..282e08a9 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonActivityFacetsTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonActivityFacetsTest.kt @@ -24,7 +24,6 @@ class ProcessJsonActivityFacetsTest { @Test fun `multi-instance loop characteristics reach the json for every engine`() { - // when val documents = sendNewsletterPerEngine() @@ -45,7 +44,6 @@ class ProcessJsonActivityFacetsTest { @Test fun `io mappings reach the json for every engine`() { - // when val documents = sendNewsletterPerEngine() @@ -60,7 +58,6 @@ class ProcessJsonActivityFacetsTest { @Test fun `the zeebe output collection binding is preserved verbatim`() { - // given: only Zeebe models an output collection, so it is asserted on its own val document = sendNewsletterPerEngine().getValue(ProcessEngine.ZEEBE) @@ -73,7 +70,6 @@ class ProcessJsonActivityFacetsTest { @Test fun `activities without either facet omit both fields`() { - // when val document = sendNewsletterPerEngine().getValue(ProcessEngine.ZEEBE) @@ -86,13 +82,11 @@ class ProcessJsonActivityFacetsTest { /** * The send-newsletter fixture — the only one carrying both facets — generated for every engine. */ - private fun sendNewsletterPerEngine(): Map { - return mapOf( - ProcessEngine.ZEEBE to generate(ProcessEngine.ZEEBE, "c8-send-newsletter"), - ProcessEngine.CAMUNDA_7 to generate(ProcessEngine.CAMUNDA_7, "c7-send-newsletter"), - ProcessEngine.OPERATON to generate(ProcessEngine.OPERATON, "operaton-send-newsletter"), - ) - } + private fun sendNewsletterPerEngine(): Map = mapOf( + ProcessEngine.ZEEBE to generate(ProcessEngine.ZEEBE, "c8-send-newsletter"), + ProcessEngine.CAMUNDA_7 to generate(ProcessEngine.CAMUNDA_7, "c7-send-newsletter"), + ProcessEngine.OPERATON to generate(ProcessEngine.OPERATON, "operaton-send-newsletter"), + ) private fun generate(engine: ProcessEngine, fixture: String): JsonObject { val input = CreateProcessJsonInMemoryPlugin.BpmnInput( @@ -103,17 +97,13 @@ class ProcessJsonActivityFacetsTest { return Json.parseToJsonElement(generated.content).jsonObject } - private fun JsonObject.flowNode(id: String): JsonObject { - return getValue("process").jsonObject - .getValue("flowNodes").jsonArray - .map { it.jsonObject } - .single { it.text("id") == id } - } + private fun JsonObject.flowNode(id: String): JsonObject = getValue("process").jsonObject + .getValue("flowNodes").jsonArray + .map { it.jsonObject } + .single { it.text("id") == id } private fun JsonObject.text(field: String): String? = this[field]?.jsonPrimitive?.content - private fun readResource(path: String): String { - return requireNotNull(javaClass.getResourceAsStream(path)) { "missing test resource $path" } - .use { it.readBytes().decodeToString() } - } + private fun readResource(path: String): String = requireNotNull(javaClass.getResourceAsStream(path)) { "missing test resource $path" } + .use { it.readBytes().decodeToString() } } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonEndToEndTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonEndToEndTest.kt index e052f42b..1e59ee9c 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonEndToEndTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonEndToEndTest.kt @@ -2,10 +2,10 @@ package io.miragon.bpmn.adapter.outbound.json import io.miragon.bpmn.adapter.inbound.CreateProcessJsonInMemoryPlugin import io.miragon.bpmn.domain.shared.ProcessEngine -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource +import java.io.File /** * Snapshots the **whole** pipeline: a real BPMN file in, the published JSON out. @@ -29,7 +29,6 @@ class ProcessJsonEndToEndTest { "OPERATON, operaton-subscribe-newsletter", ) fun `real bpmn produces the committed json`(engine: ProcessEngine, fixture: String) { - // given: the shared fixture for this engine val input = CreateProcessJsonInMemoryPlugin.BpmnInput( bpmnXml = readResource("/bpmn/$fixture.bpmn"), @@ -52,9 +51,7 @@ class ProcessJsonEndToEndTest { return readResource(path) } - private fun readResource(path: String): String { - return requireNotNull(javaClass.getResourceAsStream(path)) { "missing resource $path" } - .bufferedReader() - .readText() - } + private fun readResource(path: String): String = requireNotNull(javaClass.getResourceAsStream(path)) { "missing resource $path" } + .bufferedReader() + .readText() } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonSchemaTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonSchemaTest.kt index 6f6819eb..9067e041 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonSchemaTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/adapter/outbound/json/ProcessJsonSchemaTest.kt @@ -35,7 +35,6 @@ class ProcessJsonSchemaTest { @ParameterizedTest @EnumSource(ProcessEngine::class) fun `generated process json conforms to the published schema`(engine: ProcessEngine) { - // when: every shared fixture of this engine runs through the real pipeline val generated = generateAll(engine) @@ -49,7 +48,6 @@ class ProcessJsonSchemaTest { @Test fun `golden json fixtures conform to the published schema`() { - // given: the committed fixtures, which also cover the merged multi-variant shape val goldenFiles = listOf( "/json/NewsletterSubscriptionProcess.json", @@ -69,7 +67,6 @@ class ProcessJsonSchemaTest { @ParameterizedTest @EnumSource(ProcessEngine::class) fun `every reference in the generated json resolves`(engine: ProcessEngine) { - // when val generated = generateAll(engine) @@ -93,7 +90,6 @@ class ProcessJsonSchemaTest { @Test fun `a message correlation key is declared once, on the message it belongs to`() { - // given: a Zeebe process whose zeebe:subscription sits on the bpmn:Message root element val input = CreateProcessJsonInMemoryPlugin.BpmnInput( bpmnXml = readResource("/bpmn/c8-subscribe-newsletter.bpmn"), @@ -111,14 +107,12 @@ class ProcessJsonSchemaTest { /** * Each fixture of [engine] run through the real pipeline, paired with its fixture name. */ - private fun generateAll(engine: ProcessEngine): List> { - return fixturesFor(engine).flatMap { fixture -> - val input = CreateProcessJsonInMemoryPlugin.BpmnInput( - bpmnXml = readResource("/bpmn/$fixture.bpmn"), - processName = fixture, - ) - underTest.execute(bpmnContents = listOf(input), engine = engine).map { fixture to it.content } - } + private fun generateAll(engine: ProcessEngine): List> = fixturesFor(engine).flatMap { fixture -> + val input = CreateProcessJsonInMemoryPlugin.BpmnInput( + bpmnXml = readResource("/bpmn/$fixture.bpmn"), + processName = fixture, + ) + underTest.execute(bpmnContents = listOf(input), engine = engine).map { fixture to it.content } } /** @@ -150,9 +144,8 @@ class ProcessJsonSchemaTest { private fun JsonNode.stringsAt(field: String): List = this[field]?.map { it.asText() } ?: emptyList() - private fun readResource(path: String): String = - requireNotNull(javaClass.getResourceAsStream(path)) { "missing test resource $path" } - .use { it.readBytes().decodeToString() } + private fun readResource(path: String): String = requireNotNull(javaClass.getResourceAsStream(path)) { "missing test resource $path" } + .use { it.readBytes().decodeToString() } private fun fixturesFor(engine: ProcessEngine): List = when (engine) { ProcessEngine.ZEEBE -> zeebeFixtures diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsServiceTest.kt index 9e816828..0a7944d0 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ExtractProcessModelsServiceTest.kt @@ -6,10 +6,10 @@ import io.miragon.bpmn.domain.BpmnResource import io.miragon.bpmn.domain.shared.FlowNodeDefinition import io.miragon.bpmn.domain.shared.ProcessEngine import io.miragon.bpmn.domain.shared.TaskImplementation -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test +import java.io.File class ExtractProcessModelsServiceTest { @@ -17,7 +17,6 @@ class ExtractProcessModelsServiceTest { @Test fun `extracts one model per resource, in the order they were given`() { - // given: two BPMN files targeting the same engine val resources = listOf(resource("c8-subscribe-newsletter.bpmn"), resource("c8-send-newsletter.bpmn")) @@ -30,7 +29,6 @@ class ExtractProcessModelsServiceTest { @Test fun `the models carry what the engine dialect resolved`() { - // given: the Camunda 8 model val models = underTest.extractProcessModels(command(listOf(resource("c8-subscribe-newsletter.bpmn")))) @@ -43,14 +41,12 @@ class ExtractProcessModelsServiceTest { @Test fun `no resources means no models`() { - // when / then: an empty input is not an error assertThat(underTest.extractProcessModels(command(emptyList()))).isEmpty() } @Test fun `the plugin wires the use case by default`() { - // when: going through the inbound entry point without injecting anything val models = ExtractProcessModelsPlugin().execute( listOf(resource("c8-subscribe-newsletter.bpmn")), @@ -63,7 +59,6 @@ class ExtractProcessModelsServiceTest { @Test fun `a broken resource fails with the file that caused it`() { - // given: one good file and one that is not XML val resources = listOf( resource("c8-subscribe-newsletter.bpmn"), diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiDeterministicOrderTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiDeterministicOrderTest.kt index ab804ff4..99d6d506 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiDeterministicOrderTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiDeterministicOrderTest.kt @@ -47,7 +47,6 @@ class GenerateProcessApiDeterministicOrderTest { @Test fun `generates byte-identical code regardless of input order`() { - // given: every permutation we want to exercise (canonical, reversed, rotated) val inputOrders = listOf( variantNames, diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryServiceTest.kt index 90901e9e..c04ac2b9 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiInMemoryServiceTest.kt @@ -23,16 +23,15 @@ class GenerateProcessApiInMemoryServiceTest { private val underTest = GenerateProcessApiInMemoryService( codeGenerator = codeGenerator, - bpmnService = bpmnService + bpmnService = bpmnService, ) @Test fun `service generates API files from BPMN content`() { - // given: BPMN content val bpmnInput = GenerateProcessApiInMemoryUseCase.BpmnInput( bpmnXml = "test", - processName = "test.bpmn" + processName = "test.bpmn", ) val expectedGeneratedFile = GeneratedApiFile( fileName = "TestProcessApi.kt", @@ -47,7 +46,7 @@ class GenerateProcessApiInMemoryServiceTest { bpmnContents = listOf(bpmnInput), packagePath = "com.example", outputLanguage = OutputLanguage.KOTLIN, - engine = ProcessEngine.ZEEBE + engine = ProcessEngine.ZEEBE, ) // when: generateProcessApi is called @@ -63,18 +62,17 @@ class GenerateProcessApiInMemoryServiceTest { @Test fun `service rejects a model that targets a different engine before generating`() { - // given: a model detected as Camunda 7 but generation requested for Operaton val bpmnInput = GenerateProcessApiInMemoryUseCase.BpmnInput( bpmnXml = "camunda", - processName = "newsletter.bpmn" + processName = "newsletter.bpmn", ) every { bpmnService.extract(any(), any()) } returns dummyModel.copy(detectedEngine = ProcessEngine.CAMUNDA_7) val command = GenerateProcessApiInMemoryUseCase.Command( bpmnContents = listOf(bpmnInput), packagePath = "com.example", outputLanguage = OutputLanguage.KOTLIN, - engine = ProcessEngine.OPERATON + engine = ProcessEngine.OPERATON, ) // when / then: it fails with a single engine-mismatch error and never generates code diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiServiceTest.kt index aca81403..8da77ca0 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessApiServiceTest.kt @@ -30,12 +30,11 @@ class GenerateProcessApiServiceTest { codeGenerator = codeGenerator, bpmnFileLoader = bpmnFileLoader, bpmnService = bpmnService, - fileSystemOutput = fileSystemOutput + fileSystemOutput = fileSystemOutput, ) @Test fun `generateProcessApi generates API file`() { - // given: a dummy BPMN resource and a command val dummyResource = BpmnResource( fileName = "dummy.bpmn", @@ -74,7 +73,6 @@ class GenerateProcessApiServiceTest { @Test fun `generateProcessApi skips a non-executable process`() { - // given: a single non-executable model val draftResource = BpmnResource(fileName = "draft.bpmn", content = "".encodeToByteArray()) every { bpmnFileLoader.loadFrom("baseDir", "*.bpmn") } returns listOf(draftResource) @@ -91,7 +89,6 @@ class GenerateProcessApiServiceTest { @Test fun `generateProcessApi generates only the executable process when mixed`() { - // given: one executable and one non-executable model val keepResource = BpmnResource(fileName = "keep.bpmn", content = "".encodeToByteArray()) val draftResource = BpmnResource(fileName = "draft.bpmn", content = "".encodeToByteArray()) @@ -118,7 +115,6 @@ class GenerateProcessApiServiceTest { @Test fun `generateProcessApi produces no output when all processes are non-executable`() { - // given: only non-executable models val draftOne = BpmnResource(fileName = "draft-1.bpmn", content = "".encodeToByteArray()) val draftTwo = BpmnResource(fileName = "draft-2.bpmn", content = "".encodeToByteArray()) @@ -157,7 +153,6 @@ class GenerateProcessApiServiceTest { private fun getExpectedModelApi() = testProcessModelApi( model = dummyModel, packagePath = "de.emaarco.example", - language = OutputLanguage.KOTLIN + language = OutputLanguage.KOTLIN, ) - } diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonInMemoryServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonInMemoryServiceTest.kt index 5b5ccae2..6e309289 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonInMemoryServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonInMemoryServiceTest.kt @@ -25,7 +25,6 @@ class GenerateProcessJsonInMemoryServiceTest { @Test fun `generateProcessJson generates JSON files from BPMN content`() { - // given: BPMN content and a mock extractor val bpmnInput = GenerateProcessJsonInMemoryUseCase.BpmnInput( bpmnXml = "test", diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonServiceTest.kt index 3b31fb91..28e409f4 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/GenerateProcessJsonServiceTest.kt @@ -31,7 +31,6 @@ class GenerateProcessJsonServiceTest { @Test fun `generateProcessJson generates JSON and writes to disk`() { - // given: a dummy BPMN resource and a command val dummyResource = BpmnResource(fileName = "dummy.bpmn", content = "".encodeToByteArray()) val expectedJsonFile = GeneratedJsonFile(fileName = "order.json", content = "{}") diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ValidateBpmnServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ValidateBpmnServiceTest.kt index ba743530..55cd2ea3 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ValidateBpmnServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/application/service/ValidateBpmnServiceTest.kt @@ -35,7 +35,6 @@ class ValidateBpmnServiceTest { @Test fun `valid model returns empty result`() { - // given: a valid model whose detected engine matches the selected one every { bpmnFileLoader.loadFrom(any(), any()) } returns listOf(dummyResource) every { bpmnExtractor.extract(any(), any()) } returns testProcessModel(detectedEngine = ProcessEngine.ZEEBE) @@ -50,7 +49,6 @@ class ValidateBpmnServiceTest { @Test fun `pre-merge error stops execution and returns early`() { - // given: a model with a service task missing implementation (pre-merge ERROR) val invalidModel = testProcessModel( flowNodes = listOf( @@ -58,8 +56,8 @@ class ValidateBpmnServiceTest { id = "task1", kind = TaskKind.SERVICE, implementation = TaskImplementation.Unspecified, - ) - ) + ), + ), ) every { bpmnFileLoader.loadFrom(any(), any()) } returns listOf(dummyResource) every { bpmnExtractor.extract(any(), any()) } returns invalidModel diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/BpmnModelApiTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/BpmnModelApiTest.kt index f5a33aea..6da775b4 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/BpmnModelApiTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/BpmnModelApiTest.kt @@ -13,7 +13,6 @@ class BpmnModelApiTest { @Test fun `fileName returns PascalCase class name regardless of ID separator style`() { - // given: the expected file name val expectedFileName = "NewsletterSubscriptionProcessApi" diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/BpmnValidationServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/BpmnValidationServiceTest.kt index 6ba9fd2d..422e9d8b 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/BpmnValidationServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/BpmnValidationServiceTest.kt @@ -26,7 +26,6 @@ class BpmnValidationServiceTest { @Test fun `valid model passes all pre-merge rules`() { - // given: a valid BPMN model whose detected engine matches the selected one val model = testProcessModel(detectedEngine = ProcessEngine.ZEEBE) @@ -36,7 +35,6 @@ class BpmnValidationServiceTest { @Test fun `throws BpmnValidationException for missing service task implementation`() { - // given: a model with a service task that has no implementation val model = testProcessModel( flowNodes = listOf(serviceTaskWithoutImplementation("task1")), @@ -53,10 +51,9 @@ class BpmnValidationServiceTest { @Test fun `disabled rule is skipped during validation`() { - // given: a service with the implementation rule disabled and a model that would violate it val underTest = BpmnValidationService( - ValidationConfig(disabledRules = setOf("missing-service-task-implementation")) + ValidationConfig(disabledRules = setOf("missing-service-task-implementation")), ) val model = testProcessModel( flowNodes = listOf(serviceTaskWithoutImplementation("task1")), @@ -68,7 +65,6 @@ class BpmnValidationServiceTest { @Test fun `warnings do not throw by default`() { - // given: a model that produces only warnings (empty process) val model = testProcessModel(flowNodes = emptyList()) @@ -78,7 +74,6 @@ class BpmnValidationServiceTest { @Test fun `failOnWarning promotes warnings to failures`() { - // given: a service with failOnWarning and a model with an empty process val underTest = BpmnValidationService(ValidationConfig(failOnWarning = true)) val model = testProcessModel(flowNodes = emptyList()) @@ -94,10 +89,9 @@ class BpmnValidationServiceTest { @Test fun `throws BpmnValidationException for flow node with null element id`() { - // given: a model containing a flow node without an ID val model = testProcessModel( - flowNodes = listOf(FlowNodeDefinition.Unknown(id = null)) + flowNodes = listOf(FlowNodeDefinition.Unknown(id = null)), ) // when: validating pre-merge @@ -113,13 +107,12 @@ class BpmnValidationServiceTest { @Test fun `post-merge collision detection detects collisions`() { - // given: a model with two flow nodes that produce the same constant name val model = testProcessModel( flowNodes = listOf( FlowNodeDefinition.Unknown(id = "endEvent_complete"), FlowNodeDefinition.Unknown(id = "endEvent-complete"), - ) + ), ) // when: validating post-merge @@ -133,14 +126,13 @@ class BpmnValidationServiceTest { @Test fun `post-merge collision detection detects folding collisions`() { - // given: two flow nodes whose ids keep distinct constants but fold to the same // PascalCase object name — previously emitted non-compiling generated code val model = testProcessModel( flowNodes = listOf( FlowNodeDefinition.Unknown(id = "foo"), FlowNodeDefinition.Unknown(id = "-foo"), - ) + ), ) // when: validating post-merge @@ -154,16 +146,15 @@ class BpmnValidationServiceTest { @Test fun `mandatory collision-detection rule stays active even when disabled`() { - // given: a service that tries to disable the mandatory collision-detection rule val underTest = BpmnValidationService( - ValidationConfig(disabledRules = setOf("collision-detection")) + ValidationConfig(disabledRules = setOf("collision-detection")), ) val model = testProcessModel( flowNodes = listOf( FlowNodeDefinition.Unknown(id = "endEvent_complete"), FlowNodeDefinition.Unknown(id = "endEvent-complete"), - ) + ), ) // when: validating post-merge @@ -177,13 +168,12 @@ class BpmnValidationServiceTest { @Test fun `mandatory missing-element-id rule stays active even when disabled`() { - // given: a service that tries to disable the mandatory missing-element-id rule val underTest = BpmnValidationService( - ValidationConfig(disabledRules = setOf("missing-element-id")) + ValidationConfig(disabledRules = setOf("missing-element-id")), ) val model = testProcessModel( - flowNodes = listOf(FlowNodeDefinition.Unknown(id = null)) + flowNodes = listOf(FlowNodeDefinition.Unknown(id = null)), ) // when: validating pre-merge diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/CollisionDetectionServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/CollisionDetectionServiceTest.kt index 0db11232..83e8c949 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/CollisionDetectionServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/CollisionDetectionServiceTest.kt @@ -18,7 +18,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions returns empty when no collisions exist`() { - // given: a model with distinct constant names across all element types val model = testProcessModel( processId = "TestProcess", @@ -40,7 +39,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions allows true duplicates with same original ID`() { - // given: a model with exact duplicate elements (same id) val model = testProcessModel( processId = "TestProcess", @@ -60,7 +58,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collision with case variation in FlowNodes`() { - // given: two flow nodes that differ only in case val model = testProcessModel( processId = "TestProcess", @@ -83,7 +80,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collision with separator variation in FlowNodes`() { - // given: two flow nodes that differ only in separator character val model = testProcessModel( processId = "TestProcess", @@ -108,7 +104,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collision with mixed case and separator variation`() { - // given: three flow nodes that all normalize to the same constant val model = testProcessModel( processId = "TestProcess", @@ -135,7 +130,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects folding collision that UPPER_SNAKE misses`() { - // given: two flow nodes whose ids keep distinct constants (FOO, _FOO) but fold to the // same PascalCase object name (Foo) used for Variables/CallActivities objects val model = testProcessModel( @@ -158,7 +152,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions does not double-report a collision that surfaces on both bases`() { - // given: two flow nodes that collide in UPPER_SNAKE and in PascalCase folding val model = testProcessModel( processId = "TestProcess", @@ -178,7 +171,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects UPPER_SNAKE collision that folding misses`() { - // given: two flow nodes that share a constant (FOO_BAR) but keep distinct PascalCase names val model = testProcessModel( processId = "TestProcess", @@ -199,7 +191,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collisions in Messages`() { - // given: two messages that normalize to the same constant val model = testProcessModel( processId = "TestProcess", @@ -220,7 +211,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collisions in ServiceTasks`() { - // given: two service tasks with implementations that normalize to the same constant val model = testProcessModel( processId = "TestProcess", @@ -241,7 +231,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collisions in Signals`() { - // given: two signals that normalize to the same constant val model = testProcessModel( processId = "TestProcess", @@ -262,7 +251,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collisions in Errors`() { - // given: two errors that normalize to the same constant val model = testProcessModel( processId = "TestProcess", @@ -283,7 +271,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collisions in Timers`() { - // given: two timer event nodes whose ids normalize to the same constant. Timer definitions are // now keyed by their carrying node's id, so this necessarily surfaces a FlowNode collision too. val model = testProcessModel( @@ -314,7 +301,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects collisions in Variables`() { - // given: two nodes with variables that normalize to the same constant val model = testProcessModel( processId = "TestProcess", @@ -341,7 +327,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions detects multiple collisions across different variable types`() { - // given: a model with collisions in FlowNodes, Messages, and Signals simultaneously val model = testProcessModel( processId = "TestProcess", @@ -373,7 +358,6 @@ class CollisionDetectionServiceTest { @Test fun `findCollisions handles mixed valid and collision cases`() { - // given: a model where most nodes are unique but two share a constant name val model = testProcessModel( processId = "TestProcess", diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/ModelMergerServiceTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/ModelMergerServiceTest.kt index c86a5201..3ed87dab 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/ModelMergerServiceTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/service/ModelMergerServiceTest.kt @@ -20,7 +20,6 @@ class ModelMergerServiceTest { @Test fun `merges processes with same id into ProcessModel`() { - // given: two models with same processId and one with different processId val firstFlowNode = jobWorkerTask(id = "create-order", jobType = "firstTaskType") val secondFlowNode = jobWorkerTask(id = "update-order", jobType = "secondTaskType") @@ -76,7 +75,6 @@ class ModelMergerServiceTest { @Test fun `sorts all collections alphabetically by raw name`() { - // given: model with unsorted elements val model = testProcessModel( processId = "test-process", @@ -110,7 +108,6 @@ class ModelMergerServiceTest { @Test fun `deduplicates all elements within single BPMN model`() { - // given: a single model with duplicates of various element types val timerFlowNode = FlowNodeDefinition.Event( id = "TIMER_1", @@ -157,7 +154,6 @@ class ModelMergerServiceTest { @Test fun `deduplicates shared elements across multiple BPMN models with same process ID`() { - // given: two models with overlapping elements val firstModel = testProcessModel( processId = "test-process", @@ -225,7 +221,6 @@ class ModelMergerServiceTest { @Test fun `preserves per-variant sequence flows and flow nodes`() { - // given: two models with the same processId but different flows val sharedNode = FlowNodeDefinition.Unknown(id = "Gateway_Route") val flowDeOnly = SequenceFlowDefinition("Flow_DE", "Gateway_Route", "Task_DE", conditionExpression = "country=DE") @@ -271,7 +266,6 @@ class ModelMergerServiceTest { @Test fun `unions additionalInputVariables across variants on a shared flow node`() { - // given: two variants both define the same message start event with different additional input variables val variantA = testProcessModel( processId = "order-process", @@ -325,7 +319,6 @@ class ModelMergerServiceTest { @Test fun `preserves variables on a flow node that exists only in one variant`() { - // given: a node that exists only in variantB val variantA = testProcessModel( processId = "order-process", @@ -355,7 +348,6 @@ class ModelMergerServiceTest { @Test fun `orders variants and base node selection deterministically regardless of input order`() { - // given: three variants of one process, each providing a different name for the shared node fun variant(name: String) = testProcessModel( processId = "order-process", @@ -381,7 +373,6 @@ class ModelMergerServiceTest { @Test fun `returns a single-file process without variants`() { - // given: a single model val flow = SequenceFlowDefinition("Flow_1", "Start", "End") val model = testProcessModel( @@ -401,7 +392,6 @@ class ModelMergerServiceTest { @Test fun `throws when multiple models share processId without variantName`() { - // given: two models with same processId but no variantName val model1 = testProcessModel(processId = "order-process") val model2 = testProcessModel(processId = "order-process") @@ -415,7 +405,6 @@ class ModelMergerServiceTest { @Test fun `keeps root elements that share a name but have their own id`() { - // given: a model whose modeller created two bpmn:Message elements with the same name — the common // result of typing the same name on two events instead of picking the existing message val model = testProcessModel( @@ -440,7 +429,6 @@ class ModelMergerServiceTest { @Test fun `reports a merged process as non-executable when no variant is executable`() { - // given: two variants of one process, both marked isExecutable="false" val first = testProcessModel(processId = "order-process", variantName = "de").copy(isExecutable = false) val second = testProcessModel(processId = "order-process", variantName = "en").copy(isExecutable = false) @@ -454,7 +442,6 @@ class ModelMergerServiceTest { @Test fun `reports a merged process as executable when at least one variant is`() { - // given val first = testProcessModel(processId = "order-process", variantName = "de").copy(isExecutable = false) val second = testProcessModel(processId = "order-process", variantName = "en") @@ -468,7 +455,6 @@ class ModelMergerServiceTest { @Test fun `deduplicates root elements that repeat across variants`() { - // given: two variants that both reference the same root elements val shared = RootElementDefinition.Message(id = "Message_1", name = "OrderPlaced") val first = testProcessModel(processId = "order-process", variantName = "de", messages = listOf(shared)) @@ -483,7 +469,6 @@ class ModelMergerServiceTest { @Test fun `throws when some models have variantName and some do not`() { - // given: mixed variantName presence val model1 = testProcessModel(processId = "order-process", variantName = "prodDe") val model2 = testProcessModel(processId = "order-process") diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/ValidationResultTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/ValidationResultTest.kt index 2a860d62..f093d1e5 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/ValidationResultTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/ValidationResultTest.kt @@ -21,7 +21,7 @@ class ValidationResultTest { @Test fun `result with errors has failures regardless of failOnWarning`() { val result = ValidationResult( - listOf(violation(Severity.ERROR)) + listOf(violation(Severity.ERROR)), ) assertThat(result.isValid).isFalse() assertThat(result.hasErrors).isTrue() @@ -34,7 +34,7 @@ class ValidationResultTest { @Test fun `result with only warnings does not fail by default`() { val result = ValidationResult( - listOf(violation(Severity.WARN)) + listOf(violation(Severity.WARN)), ) assertThat(result.isValid).isFalse() assertThat(result.hasErrors).isFalse() @@ -46,7 +46,7 @@ class ValidationResultTest { @Test fun `result with only warnings fails when failOnWarning is true`() { val result = ValidationResult( - listOf(violation(Severity.WARN)) + listOf(violation(Severity.WARN)), ) assertThat(result.hasFailures(failOnWarning = true)).isTrue() } @@ -54,7 +54,7 @@ class ValidationResultTest { @Test fun `result with mixed errors and warnings`() { val result = ValidationResult( - listOf(violation(Severity.ERROR), violation(Severity.WARN), violation(Severity.WARN)) + listOf(violation(Severity.ERROR), violation(Severity.WARN), violation(Severity.WARN)), ) assertThat(result.errors).hasSize(1) assertThat(result.warnings).hasSize(2) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRuleTest.kt index 5626850d..8cc277ba 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CallActivityTargetExistsRuleTest.kt @@ -25,7 +25,6 @@ class CallActivityTargetExistsRuleTest { @Test fun `reports error when the called process is absent from the loaded models`() { - // given: a caller referencing a process that was not loaded val model = caller(processId = "orderFulfillment", callId = "call1", calledElement = "paymentProcessing") @@ -41,7 +40,6 @@ class CallActivityTargetExistsRuleTest { @Test fun `no violations when the called process is present among the loaded models`() { - // given: both the caller and the called process are loaded val caller = caller(processId = "orderFulfillment", callId = "call1", calledElement = "paymentProcessing") val called = testProcessModel(processId = "paymentProcessing") @@ -55,7 +53,6 @@ class CallActivityTargetExistsRuleTest { @Test fun `ignores call activities without a called element`() { - // given: a call activity with no called element - the concern of MissingCalledElementRule val model = caller(processId = "orderFulfillment", callId = "call1", calledElement = null) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CollisionDetectionRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CollisionDetectionRuleTest.kt index 55938a1b..fd2732a7 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CollisionDetectionRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/CollisionDetectionRuleTest.kt @@ -20,13 +20,12 @@ class CollisionDetectionRuleTest { @Test fun `reports collision when different IDs normalize to same constant`() { - // given: two flow nodes that differ only in separator val model = testProcessModel( flowNodes = listOf( FlowNodeDefinition.Unknown(id = "endEvent_complete"), FlowNodeDefinition.Unknown(id = "endEvent-complete"), - ) + ), ) // when: validating @@ -40,14 +39,13 @@ class CollisionDetectionRuleTest { @Test fun `reports collision when different IDs fold to the same object name`() { - // given: two flow nodes whose ids keep distinct constants but fold to the same // PascalCase object name (previously emitted two non-compiling `object Foo`) val model = testProcessModel( flowNodes = listOf( FlowNodeDefinition.Unknown(id = "foo"), FlowNodeDefinition.Unknown(id = "-foo"), - ) + ), ) // when: validating @@ -61,13 +59,12 @@ class CollisionDetectionRuleTest { @Test fun `no violations when no collisions`() { - // given: two flow nodes with distinct constant names val model = testProcessModel( flowNodes = listOf( FlowNodeDefinition.Unknown(id = "Activity_One"), FlowNodeDefinition.Unknown(id = "Activity_Two"), - ) + ), ) // when / then: no violations diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRuleTest.kt index 64eb0f44..a93ceced 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EmptyProcessRuleTest.kt @@ -14,7 +14,6 @@ class EmptyProcessRuleTest { @Test fun `reports warning for process with no elements`() { - // given: a model with no flow nodes val model = testProcessModel(flowNodes = emptyList()) @@ -26,10 +25,9 @@ class EmptyProcessRuleTest { @Test fun `no violations for process with elements`() { - // given: a model with at least one flow node val model = testProcessModel( - flowNodes = listOf(FlowNodeDefinition.Unknown(id = "Activity_Task1")) + flowNodes = listOf(FlowNodeDefinition.Unknown(id = "Activity_Task1")), ) // when / then: no violations diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRuleTest.kt index 19397313..eb351beb 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/EngineMismatchRuleTest.kt @@ -13,7 +13,6 @@ class EngineMismatchRuleTest { @Test fun `reports an error when the model targets a different engine`() { - // given: a model detected as Camunda 7 but validated for Operaton (the reported case) val model = testProcessModel(detectedEngine = ProcessEngine.CAMUNDA_7) @@ -27,7 +26,6 @@ class EngineMismatchRuleTest { @Test fun `no violation when the detected engine matches the selected engine`() { - // given: a model whose detected engine matches the selected one val model = testProcessModel(detectedEngine = ProcessEngine.ZEEBE) @@ -37,7 +35,6 @@ class EngineMismatchRuleTest { @Test fun `warns when the source engine could not be detected`() { - // given: a model whose target engine could not be determined val model = testProcessModel(detectedEngine = null) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRuleTest.kt index 284f0d68..0a09a6f2 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingCalledElementRuleTest.kt @@ -15,7 +15,6 @@ class MissingCalledElementRuleTest { @Test fun `reports error for call activity with null calledElement`() { - // given: a call activity with no calledElement set val model = testProcessModel( flowNodes = listOf( @@ -35,7 +34,6 @@ class MissingCalledElementRuleTest { @Test fun `no violations for call activity with calledElement`() { - // given: a call activity with a valid calledElement reference val model = testProcessModel( flowNodes = listOf( diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingElementIdRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingElementIdRuleTest.kt index 4086428e..db615e2f 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingElementIdRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingElementIdRuleTest.kt @@ -14,10 +14,9 @@ class MissingElementIdRuleTest { @Test fun `reports error for flow node with null id`() { - // given: a model containing a flow node without an ID val model = testProcessModel( - flowNodes = listOf(FlowNodeDefinition.Unknown(id = null)) + flowNodes = listOf(FlowNodeDefinition.Unknown(id = null)), ) // when / then: an ERROR violation mentioning "FlowNode has no ID" @@ -29,10 +28,9 @@ class MissingElementIdRuleTest { @Test fun `no violations for elements with valid ids`() { - // given: a flow node with a valid ID val model = testProcessModel( - flowNodes = listOf(FlowNodeDefinition.Unknown(id = "Activity_SendMail")) + flowNodes = listOf(FlowNodeDefinition.Unknown(id = "Activity_SendMail")), ) // when / then: no violations diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRuleTest.kt index dd1b5115..c6f2df6a 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingErrorDefinitionRuleTest.kt @@ -14,19 +14,16 @@ class MissingErrorDefinitionRuleTest { private val underTest = MissingErrorDefinitionRule() - private fun errorEvent(id: String, errorRef: String?, errorName: String?, errorCode: String?) = - FlowNodeDefinition.Event( - id = id, - shape = EventShape.END_EVENT, - eventDefinitions = listOf(EventDefinitionInstance.Error(errorRef, errorName, errorCode)), - ) + private fun errorEvent(id: String, errorRef: String?, errorName: String?, errorCode: String?) = FlowNodeDefinition.Event( + id = id, + shape = EventShape.END_EVENT, + eventDefinitions = listOf(EventDefinitionInstance.Error(errorRef, errorName, errorCode)), + ) - private fun validate(node: FlowNodeDefinition) = - underTest.validate(SingleModelValidationContext(model = testProcessModel(flowNodes = listOf(node)), engine = ProcessEngine.ZEEBE)) + private fun validate(node: FlowNodeDefinition) = underTest.validate(SingleModelValidationContext(model = testProcessModel(flowNodes = listOf(node)), engine = ProcessEngine.ZEEBE)) @Test fun `reports error for an error event whose definition has no name`() { - // given: an error event referencing an error root element but carrying no name val node = errorEvent(id = "errorEnd1", errorRef = "Error_1", errorName = null, errorCode = "500") @@ -39,7 +36,6 @@ class MissingErrorDefinitionRuleTest { @Test fun `reports error for an error event whose definition has no code`() { - // given: an error event referencing an error root element but carrying no code val node = errorEvent(id = "errorEnd1", errorRef = "Error_1", errorName = "MyError", errorCode = null) @@ -50,7 +46,6 @@ class MissingErrorDefinitionRuleTest { @Test fun `no violations for an error event with all fields`() { - // given: a fully defined error event val node = errorEvent(id = "errorEnd1", errorRef = "Error_1", errorName = "MyError", errorCode = "500") @@ -60,7 +55,6 @@ class MissingErrorDefinitionRuleTest { @Test fun `does not flag a catch-all error event without an errorRef`() { - // given: an error boundary event that catches any error (no errorRef, no name, no code) val node = errorEvent(id = "catchAll", errorRef = null, errorName = null, errorCode = null) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRuleTest.kt index 85bf5878..87f229b1 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingMessageNameRuleTest.kt @@ -16,12 +16,10 @@ class MissingMessageNameRuleTest { private val underTest = MissingMessageNameRule() - private fun validate(node: FlowNodeDefinition) = - underTest.validate(SingleModelValidationContext(model = testProcessModel(flowNodes = listOf(node)), engine = ProcessEngine.ZEEBE)) + private fun validate(node: FlowNodeDefinition) = underTest.validate(SingleModelValidationContext(model = testProcessModel(flowNodes = listOf(node)), engine = ProcessEngine.ZEEBE)) @Test fun `reports error for a message event whose message has no name`() { - // given: a message catch event whose message reference carries no name val node = FlowNodeDefinition.Event( id = "msgEvent1", @@ -38,7 +36,6 @@ class MissingMessageNameRuleTest { @Test fun `no violations for a message event with a valid name`() { - // given: a message catch event whose message reference has a name val node = FlowNodeDefinition.Event( id = "msgEvent1", @@ -52,7 +49,6 @@ class MissingMessageNameRuleTest { @Test fun `does not flag a message throw event that carries no message reference`() { - // given: a Zeebe message end event that publishes via a job worker, with an empty message reference val node = FlowNodeDefinition.Event( id = "msgThrow1", @@ -66,7 +62,6 @@ class MissingMessageNameRuleTest { @Test fun `reports error for a send task whose message has no name`() { - // given: a send task that references a message with no name val node = FlowNodeDefinition.Activity.Task( id = "send1", @@ -82,7 +77,6 @@ class MissingMessageNameRuleTest { @Test fun `does not flag a send task with no message`() { - // given: a send task that carries no message at all - not this rule's concern val node = FlowNodeDefinition.Activity.Task(id = "send1", kind = TaskKind.SEND, message = null) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRuleTest.kt index 4e06f7e6..4cc498f9 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingProcessIdRuleTest.kt @@ -13,7 +13,6 @@ class MissingProcessIdRuleTest { @Test fun `reports error for blank process id`() { - // given: a model with an empty process ID val model = testProcessModel(processId = "") @@ -25,7 +24,6 @@ class MissingProcessIdRuleTest { @Test fun `no violations for valid process id`() { - // given: a model with a non-blank process ID val model = testProcessModel(processId = "my-process") diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRuleTest.kt index 7b099792..20d36147 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingServiceTaskImplementationRuleTest.kt @@ -25,7 +25,6 @@ class MissingServiceTaskImplementationRuleTest { @Test fun `reports error for service task with no implementation`() { - // given: a service task with no implementation val model = testProcessModel(flowNodes = listOf(unimplementedServiceTask("task1"))) @@ -41,14 +40,13 @@ class MissingServiceTaskImplementationRuleTest { @Test fun `reports every unimplemented service task, not just the first`() { - // given: three service tasks that all lack an implementation val model = testProcessModel( flowNodes = listOf( unimplementedServiceTask("task1"), unimplementedServiceTask("task2"), unimplementedServiceTask("task3"), - ) + ), ) // when @@ -60,7 +58,6 @@ class MissingServiceTaskImplementationRuleTest { @Test fun `no violations for service task with valid implementation`() { - // given: a service task with a resolved job-worker implementation val model = testProcessModel( flowNodes = listOf( @@ -79,7 +76,6 @@ class MissingServiceTaskImplementationRuleTest { @Test fun `engine-specific hint for Camunda 7`() { - // given: a service task with no implementation validated against Camunda 7 val model = testProcessModel(flowNodes = listOf(unimplementedServiceTask("task1"))) @@ -90,7 +86,6 @@ class MissingServiceTaskImplementationRuleTest { @Test fun `engine-specific hint for Operaton`() { - // given: a service task with no implementation validated against Operaton val model = testProcessModel(flowNodes = listOf(unimplementedServiceTask("task1"))) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRuleTest.kt index 60880e0e..0c252da7 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingSignalNameRuleTest.kt @@ -14,10 +14,9 @@ class MissingSignalNameRuleTest { @Test fun `reports error for signal with null name`() { - // given: a signal element with no name val model = testProcessModel( - signals = listOf(RootElementDefinition.Signal(id = "sig1", name = null)) + signals = listOf(RootElementDefinition.Signal(id = "sig1", name = null)), ) // when / then: an ERROR violation is reported @@ -28,10 +27,9 @@ class MissingSignalNameRuleTest { @Test fun `no violations for signal with name`() { - // given: a signal element with a valid name val model = testProcessModel( - signals = listOf(RootElementDefinition.Signal(id = "sig1", name = "MySignal")) + signals = listOf(RootElementDefinition.Signal(id = "sig1", name = "MySignal")), ) // when / then: no violations diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRuleTest.kt index 4add6001..463a6d09 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/MissingTimerDefinitionRuleTest.kt @@ -17,7 +17,6 @@ class MissingTimerDefinitionRuleTest { @Test fun `reports error for timer with no type`() { - // given: a timer event carrying a definition with neither a type nor an expression val model = testProcessModel( flowNodes = listOf( @@ -38,7 +37,6 @@ class MissingTimerDefinitionRuleTest { @Test fun `no violations for timer with type and expression`() { - // given: a timer event with a valid type and expression val model = testProcessModel( flowNodes = listOf( diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtMessageThrowRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtMessageThrowRuleTest.kt index 04d55287..a3c66efa 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtMessageThrowRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtMessageThrowRuleTest.kt @@ -26,15 +26,12 @@ class UncaughtMessageThrowRuleTest { private fun catchNode(id: String, message: String) = messageEvent(id, message, EventShape.INTERMEDIATE_CATCH_EVENT) - private fun model(processId: String, vararg nodes: FlowNodeDefinition) = - testProcessModel(processId = processId, flowNodes = nodes.toList()) + private fun model(processId: String, vararg nodes: FlowNodeDefinition) = testProcessModel(processId = processId, flowNodes = nodes.toList()) - private fun validate(vararg models: ProcessModel) = - underTest.validate(CrossModelValidationContext(models = models.toList(), engine = ProcessEngine.ZEEBE)) + private fun validate(vararg models: ProcessModel) = underTest.validate(CrossModelValidationContext(models = models.toList(), engine = ProcessEngine.ZEEBE)) @Test fun `warns on a thrown message that is never caught in the fileset`() { - // given: a single model that throws a message no one catches val thrower = model("orderPlacement", throwNode("throw1", "OrderShipped")) @@ -51,7 +48,6 @@ class UncaughtMessageThrowRuleTest { @Test fun `no warning when a catcher exists in the same model`() { - // given: the throw and a matching catch live in one model val model = model("orderPlacement", throwNode("throw1", "OrderShipped"), catchNode("catch1", "OrderShipped")) @@ -64,7 +60,6 @@ class UncaughtMessageThrowRuleTest { @Test fun `no warning when the catcher lives in another loaded model`() { - // given: the message is thrown in one process and caught in another - the cross-model case val thrower = model("orderPlacement", throwNode("throw1", "OrderShipped")) val catcher = model("shipping", catchNode("catch1", "OrderShipped")) @@ -78,7 +73,6 @@ class UncaughtMessageThrowRuleTest { @Test fun `warns only for the message whose catcher is missing`() { - // given: two thrown messages, only one of which is caught anywhere val thrower = model("orderPlacement", throwNode("throw1", "OrderShipped"), throwNode("throw2", "OrderCancelled")) val catcher = model("shipping", catchNode("catch1", "OrderShipped")) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtSignalThrowRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtSignalThrowRuleTest.kt index e217d9fb..cb643f94 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtSignalThrowRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UncaughtSignalThrowRuleTest.kt @@ -25,15 +25,12 @@ class UncaughtSignalThrowRuleTest { private fun catchNode(id: String, signal: String) = signalEvent(id, signal, EventShape.INTERMEDIATE_CATCH_EVENT) - private fun model(processId: String, vararg nodes: FlowNodeDefinition) = - testProcessModel(processId = processId, flowNodes = nodes.toList()) + private fun model(processId: String, vararg nodes: FlowNodeDefinition) = testProcessModel(processId = processId, flowNodes = nodes.toList()) - private fun validate(vararg models: ProcessModel) = - underTest.validate(CrossModelValidationContext(models = models.toList(), engine = ProcessEngine.ZEEBE)) + private fun validate(vararg models: ProcessModel) = underTest.validate(CrossModelValidationContext(models = models.toList(), engine = ProcessEngine.ZEEBE)) @Test fun `warns on a thrown signal that is never caught in the fileset`() { - // given: a single model that throws a signal no one subscribes to val thrower = model("registration", throwNode("throw1", "RegistrationBlocked")) @@ -50,7 +47,6 @@ class UncaughtSignalThrowRuleTest { @Test fun `no warning when a catcher exists in the same model`() { - // given: the throw and a matching catch live in one model val model = model("registration", throwNode("throw1", "RegistrationBlocked"), catchNode("catch1", "RegistrationBlocked")) @@ -63,7 +59,6 @@ class UncaughtSignalThrowRuleTest { @Test fun `no warning when the catcher lives in another loaded model`() { - // given: the signal is thrown in one process and caught in another - the cross-model case val thrower = model("registration", throwNode("throw1", "RegistrationBlocked")) val catcher = model("monitoring", catchNode("catch1", "RegistrationBlocked")) @@ -77,7 +72,6 @@ class UncaughtSignalThrowRuleTest { @Test fun `warns only for the signal whose catcher is missing`() { - // given: two thrown signals, only one of which is caught anywhere val thrower = model("registration", throwNode("throw1", "RegistrationBlocked"), throwNode("throw2", "AccountLocked")) val catcher = model("monitoring", catchNode("catch1", "RegistrationBlocked")) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnpublishedSignalCatchRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnpublishedSignalCatchRuleTest.kt index bd64c7b9..c8467d69 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnpublishedSignalCatchRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnpublishedSignalCatchRuleTest.kt @@ -25,15 +25,12 @@ class UnpublishedSignalCatchRuleTest { private fun catchNode(id: String, signal: String) = signalEvent(id, signal, EventShape.INTERMEDIATE_CATCH_EVENT) - private fun model(processId: String, vararg nodes: FlowNodeDefinition) = - testProcessModel(processId = processId, flowNodes = nodes.toList()) + private fun model(processId: String, vararg nodes: FlowNodeDefinition) = testProcessModel(processId = processId, flowNodes = nodes.toList()) - private fun validate(vararg models: ProcessModel) = - underTest.validate(CrossModelValidationContext(models = models.toList(), engine = ProcessEngine.ZEEBE)) + private fun validate(vararg models: ProcessModel) = underTest.validate(CrossModelValidationContext(models = models.toList(), engine = ProcessEngine.ZEEBE)) @Test fun `warns on a caught signal that is never thrown in the fileset`() { - // given: a single model that subscribes to a signal no one publishes val subscriber = model("monitoring", catchNode("catch1", "RegistrationBlocked")) @@ -50,7 +47,6 @@ class UnpublishedSignalCatchRuleTest { @Test fun `no warning when a thrower exists in the same model`() { - // given: the catch and a matching throw live in one model val model = model("monitoring", catchNode("catch1", "RegistrationBlocked"), throwNode("throw1", "RegistrationBlocked")) @@ -63,7 +59,6 @@ class UnpublishedSignalCatchRuleTest { @Test fun `no warning when the thrower lives in another loaded model`() { - // given: the signal is caught in one process and thrown in another - the cross-model case val subscriber = model("monitoring", catchNode("catch1", "RegistrationBlocked")) val publisher = model("registration", throwNode("throw1", "RegistrationBlocked")) @@ -77,7 +72,6 @@ class UnpublishedSignalCatchRuleTest { @Test fun `warns only for the signal whose thrower is missing`() { - // given: two caught signals, only one of which is thrown anywhere val subscriber = model("monitoring", catchNode("catch1", "RegistrationBlocked"), catchNode("catch2", "AccountLocked")) val publisher = model("registration", throwNode("throw1", "RegistrationBlocked")) diff --git a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRuleTest.kt b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRuleTest.kt index 3f7c8e55..8d07baee 100644 --- a/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRuleTest.kt +++ b/bpmn-to-code-core/src/test/kotlin/io/miragon/bpmn/domain/validation/rules/UnreferencedRootElementRuleTest.kt @@ -25,7 +25,6 @@ class UnreferencedRootElementRuleTest { @Test fun `warns about a message no element references`() { - // given: two declared messages, only one of which an event points at val model = testProcessModel( flowNodes = listOf(messageStartEvent("Message_Used")), @@ -49,7 +48,6 @@ class UnreferencedRootElementRuleTest { @Test fun `reports nothing when every root element is referenced`() { - // given val model = testProcessModel( flowNodes = listOf(messageStartEvent("Message_Used")), @@ -64,7 +62,6 @@ class UnreferencedRootElementRuleTest { @Test fun `covers signals and errors as well as messages`() { - // given: an unreferenced entry in each registry val model = testProcessModel( flowNodes = listOf(FlowNodeDefinition.Unknown(id = "node")), @@ -83,7 +80,6 @@ class UnreferencedRootElementRuleTest { @Test fun `counts a message referenced by a receive task as used`() { - // given: send and receive tasks reference their message directly, not through an event definition val receiveTask = FlowNodeDefinition.Activity.Task( id = "Activity_Await", diff --git a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/BpmnModelGeneratorPlugin.kt b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/BpmnModelGeneratorPlugin.kt index 25ab2573..08c0c679 100644 --- a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/BpmnModelGeneratorPlugin.kt +++ b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/BpmnModelGeneratorPlugin.kt @@ -1,9 +1,9 @@ package io.miragon.bpmn.adapter -import java.util.Properties import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project +import java.util.Properties @Suppress("unused") class BpmnModelGeneratorPlugin : Plugin { @@ -42,7 +42,7 @@ class BpmnModelGeneratorPlugin : Plugin { val stream = javaClass.classLoader.getResourceAsStream(VERSION_RESOURCE) ?: throw GradleException( "[bpmn-to-code] Could not locate '$VERSION_RESOURCE' on the plugin classpath. " + - "This is a bug in the plugin distribution — please report it." + "This is a bug in the plugin distribution — please report it.", ) val properties = stream.use { Properties().apply { load(it) } @@ -50,7 +50,7 @@ class BpmnModelGeneratorPlugin : Plugin { return properties.getProperty("version")?.takeIf { it.isNotBlank() } ?: throw GradleException( "[bpmn-to-code] '$VERSION_RESOURCE' is missing a non-blank 'version' entry. " + - "This is a bug in the plugin distribution — please report it." + "This is a bug in the plugin distribution — please report it.", ) } } diff --git a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnJsonTask.kt b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnJsonTask.kt index 6ac66675..83bdb2ac 100644 --- a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnJsonTask.kt +++ b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnJsonTask.kt @@ -8,7 +8,7 @@ import org.gradle.api.tasks.TaskAction import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault( - because = "Task produces output based on files that can change at any time without the plugin knowing about it" + because = "Task produces output based on files that can change at any time without the plugin knowing about it", ) abstract class GenerateBpmnJsonTask : DefaultTask() { diff --git a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnModelsTask.kt b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnModelsTask.kt index 781a6ee0..4dfcf543 100644 --- a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnModelsTask.kt +++ b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/GenerateBpmnModelsTask.kt @@ -9,7 +9,7 @@ import org.gradle.api.tasks.TaskAction import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault( - because = "Task produces output based on files that can change at any time without the plugin knowing about it" + because = "Task produces output based on files that can change at any time without the plugin knowing about it", ) abstract class GenerateBpmnModelsTask : DefaultTask() { diff --git a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/ValidateBpmnModelsTask.kt b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/ValidateBpmnModelsTask.kt index ec3a9da0..0807bcb1 100644 --- a/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/ValidateBpmnModelsTask.kt +++ b/bpmn-to-code-gradle/src/main/kotlin/io/miragon/bpmn/adapter/ValidateBpmnModelsTask.kt @@ -13,7 +13,7 @@ import org.gradle.work.DisableCachingByDefault @Incubating @DisableCachingByDefault( - because = "Validation depends on BPMN files that can change at any time without the plugin knowing about it" + because = "Validation depends on BPMN files that can change at any time without the plugin knowing about it", ) abstract class ValidateBpmnModelsTask : DefaultTask() { @@ -57,7 +57,7 @@ abstract class ValidateBpmnModelsTask : DefaultTask() { if (result.hasFailures(failOnWarning)) { throw GradleException( - "BPMN validation failed: ${result.errors.size} error(s), ${result.warnings.size} warning(s)" + "BPMN validation failed: ${result.errors.size} error(s), ${result.warnings.size} warning(s)", ) } logger.lifecycle("BPMN validation passed") @@ -69,6 +69,5 @@ abstract class ValidateBpmnModelsTask : DefaultTask() { check(this::processEngine.isInitialized) { "processEngine must be configured in bpmnToCode { ... }" } } - private fun formatLocation(v: ValidationViolation): String = - if (v.elementId != null) "${v.processId}/${v.elementId}" else v.processId + private fun formatLocation(v: ValidationViolation): String = if (v.elementId != null) "${v.processId}/${v.elementId}" else v.processId } diff --git a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginDependencyResolutionSmokeTest.kt b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginDependencyResolutionSmokeTest.kt index 542ce491..0c3ae99e 100644 --- a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginDependencyResolutionSmokeTest.kt +++ b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginDependencyResolutionSmokeTest.kt @@ -1,11 +1,11 @@ package io.miragon.bpmn.adapter -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir +import java.io.File /** * Resolves the plugin from mavenLocal (published POM) instead of using withPluginClasspath(). @@ -23,7 +23,6 @@ class GradlePluginDependencyResolutionSmokeTest { @Test fun `plugin resolves all dependencies from published artifact`(@TempDir projectDir: File) { - // given: a minimal project configured to resolve the plugin from mavenLocal val resourcesDir = File(projectDir, "src/main/resources").also { it.mkdirs() } val bpmnStream = requireNotNull(javaClass.classLoader.getResourceAsStream("bpmn/c8-subscribe-newsletter.bpmn")) @@ -37,7 +36,7 @@ class GradlePluginDependencyResolutionSmokeTest { mavenCentral() } } - """.trimIndent() + """.trimIndent(), ) File(projectDir, "build.gradle").writeText( """ @@ -53,7 +52,7 @@ class GradlePluginDependencyResolutionSmokeTest { outputLanguage = io.miragon.bpmn.domain.shared.OutputLanguage.KOTLIN processEngine = io.miragon.bpmn.domain.shared.ProcessEngine.ZEEBE } - """.trimIndent() + """.trimIndent(), ) // when: running WITHOUT withPluginClasspath() so Gradle resolves from mavenLocal @@ -74,7 +73,6 @@ class GradlePluginDependencyResolutionSmokeTest { @Test fun `applying the plugin adds bpmn-to-code-runtime to the implementation configuration`(@TempDir projectDir: File) { - // given: a Java project with the plugin resolved from mavenLocal File(projectDir, "settings.gradle").writeText( """ @@ -85,7 +83,7 @@ class GradlePluginDependencyResolutionSmokeTest { mavenCentral() } } - """.trimIndent() + """.trimIndent(), ) File(projectDir, "build.gradle").writeText( """ @@ -97,7 +95,7 @@ class GradlePluginDependencyResolutionSmokeTest { mavenLocal() mavenCentral() } - """.trimIndent() + """.trimIndent(), ) // when: inspecting the implementation dependencies @@ -112,7 +110,6 @@ class GradlePluginDependencyResolutionSmokeTest { @Test fun `generateBpmnModelJson resolves kotlinx-serialization from published artifact`(@TempDir projectDir: File) { - // given: a minimal project configured to resolve the plugin from mavenLocal val resourcesDir = File(projectDir, "src/main/resources").also { it.mkdirs() } val bpmnStream = requireNotNull(javaClass.classLoader.getResourceAsStream("bpmn/c8-subscribe-newsletter.bpmn")) @@ -126,7 +123,7 @@ class GradlePluginDependencyResolutionSmokeTest { mavenCentral() } } - """.trimIndent() + """.trimIndent(), ) File(projectDir, "build.gradle").writeText( """ @@ -140,7 +137,7 @@ class GradlePluginDependencyResolutionSmokeTest { outputFolderPath = "${'$'}{projectDir}/build/generated-json" processEngine = io.miragon.bpmn.domain.shared.ProcessEngine.ZEEBE } - """.trimIndent() + """.trimIndent(), ) // when: running WITHOUT withPluginClasspath() so Gradle resolves from mavenLocal diff --git a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginSmokeTest.kt b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginSmokeTest.kt index de783a8b..5825a6f8 100644 --- a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginSmokeTest.kt +++ b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradlePluginSmokeTest.kt @@ -1,12 +1,12 @@ package io.miragon.bpmn.adapter -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource +import java.io.File class GradlePluginSmokeTest { @@ -64,7 +64,7 @@ class GradlePluginSmokeTest { outputLanguage = io.miragon.bpmn.domain.shared.OutputLanguage.$language processEngine = io.miragon.bpmn.domain.shared.ProcessEngine.$engine } - """.trimIndent() + """.trimIndent(), ) // when: running the compile task (which depends on generateBpmnModelApi) diff --git a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradleValidationSmokeTest.kt b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradleValidationSmokeTest.kt index 6c135b95..42b20b55 100644 --- a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradleValidationSmokeTest.kt +++ b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/GradleValidationSmokeTest.kt @@ -1,12 +1,12 @@ package io.miragon.bpmn.adapter -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource +import java.io.File class GradleValidationSmokeTest { @@ -37,7 +37,7 @@ class GradleValidationSmokeTest { filePattern = 'src/main/resources/*.bpmn' processEngine = io.miragon.bpmn.domain.shared.ProcessEngine.$engine } - """.trimIndent() + """.trimIndent(), ) // when: running the validateBpmnModels task diff --git a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/MultiModuleRuntimeSharingSmokeTest.kt b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/MultiModuleRuntimeSharingSmokeTest.kt index 5f865e89..b0faf70c 100644 --- a/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/MultiModuleRuntimeSharingSmokeTest.kt +++ b/bpmn-to-code-gradle/src/test/kotlin/io/miragon/bpmn/adapter/MultiModuleRuntimeSharingSmokeTest.kt @@ -1,11 +1,11 @@ package io.miragon.bpmn.adapter -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir +import java.io.File /** * Verifies the multi-module promise: a `common` module can expose typed wrappers over the runtime's @@ -19,7 +19,6 @@ class MultiModuleRuntimeSharingSmokeTest { @Test fun `common module wrapper accepts ProcessId from two independently generated service APIs`(@TempDir projectDir: File) { - val commonDir = File(projectDir, "common").also { it.mkdirs() } val serviceADir = File(projectDir, "service-a").also { it.mkdirs() } val serviceBDir = File(projectDir, "service-b").also { it.mkdirs() } @@ -44,7 +43,7 @@ class MultiModuleRuntimeSharingSmokeTest { } rootProject.name = "multi-module-smoke" include("common", "service-a", "service-b") - """.trimIndent() + """.trimIndent(), ) File(commonDir, "build.gradle.kts").writeText( @@ -55,7 +54,7 @@ class MultiModuleRuntimeSharingSmokeTest { dependencies { implementation("io.miragon:bpmn-to-code-runtime:$pluginVersion") } - """.trimIndent() + """.trimIndent(), ) File(commonDir, "src/main/kotlin/com/acme/common/EngineGateway.kt").apply { parentFile.mkdirs() }.writeText( @@ -69,7 +68,7 @@ class MultiModuleRuntimeSharingSmokeTest { fun start(id: ProcessId): String = "started:" + id fun publish(msg: MessageName): String = "published:" + msg } - """.trimIndent() + """.trimIndent(), ) writeServiceModule(serviceADir, packagePath = "com.acme.service_a.bpmn", callerName = "UsesApiA") @@ -106,7 +105,7 @@ class MultiModuleRuntimeSharingSmokeTest { } sourceSets.main { kotlin.srcDir(generatedSrc) } tasks.named("compileKotlin") { dependsOn("generateBpmnModelApi") } - """.trimIndent() + """.trimIndent(), ) File(moduleDir, "src/main/kotlin/com/acme/$callerName.kt").apply { parentFile.mkdirs() }.writeText( @@ -121,7 +120,7 @@ class MultiModuleRuntimeSharingSmokeTest { return gateway.start(NewsletterSubscriptionProcessApi.PROCESS_ID) } } - """.trimIndent() + """.trimIndent(), ) } diff --git a/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenMojoSmokeTest.kt b/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenMojoSmokeTest.kt index bb87dd72..403dd0dd 100644 --- a/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenMojoSmokeTest.kt +++ b/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenMojoSmokeTest.kt @@ -1,10 +1,10 @@ package io.miragon.bpmn.adapter -import java.io.File import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource +import java.io.File class MavenMojoSmokeTest { diff --git a/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenValidateMojoSmokeTest.kt b/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenValidateMojoSmokeTest.kt index 0e4d0a30..8bd49589 100644 --- a/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenValidateMojoSmokeTest.kt +++ b/bpmn-to-code-maven/src/test/kotlin/io/miragon/bpmn/adapter/MavenValidateMojoSmokeTest.kt @@ -1,10 +1,10 @@ package io.miragon.bpmn.adapter -import java.io.File import org.assertj.core.api.Assertions.assertThatCode import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource +import java.io.File class MavenValidateMojoSmokeTest { diff --git a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/AbstractFlowNode.kt b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/AbstractFlowNode.kt index 232c73e8..1dc91c90 100644 --- a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/AbstractFlowNode.kt +++ b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/AbstractFlowNode.kt @@ -15,11 +15,7 @@ abstract class AbstractFlowNode( override val elementType: String, ) : FlowNode { - override fun equals(other: Any?): Boolean { - return this === other || (other is FlowNode && other.id == id) - } + override fun equals(other: Any?): Boolean = this === other || (other is FlowNode && other.id == id) - override fun hashCode(): Int { - return id.hashCode() - } + override fun hashCode(): Int = id.hashCode() } diff --git a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/PathWalk.kt b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/PathWalk.kt index 01759b35..351037fe 100644 --- a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/PathWalk.kt +++ b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/PathWalk.kt @@ -27,33 +27,28 @@ class PathWalk, NEXT> internal constructor( * Advances to a real successor and records it. `pick`'s input is the current node's `Next`, so only an * actual successor compiles. */ - fun , MNEXT> then(pick: Function): PathWalk = - PathWalk(path.then { pick.apply(it) }) + fun , MNEXT> then(pick: Function): PathWalk = PathWalk(path.then { pick.apply(it) }) /** * Records the same successor [times] times in a row — for a sequential multi-instance activity or a * consecutive self-repeat. */ - fun , MNEXT> thenMultipleTimes(times: Int, pick: Function): PathWalk = - PathWalk(path.thenMultipleTimes(repeatTimes = times) { pick.apply(it) }) + fun , MNEXT> thenMultipleTimes(times: Int, pick: Function): PathWalk = PathWalk(path.thenMultipleTimes(repeatTimes = times) { pick.apply(it) }) /** * Advances onto a subprocess node **without** recording it — positions for [enter] / [inside]. */ - fun , MNEXT> onto(pick: Function): PathWalk = - PathWalk(path.onto { pick.apply(it) }) + fun , MNEXT> onto(pick: Function): PathWalk = PathWalk(path.onto { pick.apply(it) }) /** * Terminal step: advances to a final successor (e.g. an end event) and stops, yielding a [Trail]. */ - fun end(pick: Function): Trail = - Trail(path.then { pick.apply(it) }) + fun end(pick: Function): Trail = Trail(path.then { pick.apply(it) }) /** * Descends into a named interior [scope] and records the picked inner node — the re-anchor form of enter. */ - fun , MNEXT> enter(scope: NavigationScope, pick: Function): PathWalk = - PathWalk(path.enter(inner = scope) { pick.apply(it) }) + fun , MNEXT> enter(scope: NavigationScope, pick: Function): PathWalk = PathWalk(path.enter(inner = scope) { pick.apply(it) }) /** * Walks a subprocess interior in [block] (seeded from [scope]) and then continues **on the current @@ -71,16 +66,14 @@ class PathWalk, NEXT> internal constructor( fun , MNEXT> interruptedBy( carrier: HasSuccessors, pick: Function, - ): PathWalk = - PathWalk(path.interruptedBy(carrier = carrier) { pick.apply(it) }) + ): PathWalk = PathWalk(path.interruptedBy(carrier = carrier) { pick.apply(it) }) /** * Unchecked re-anchor to an arbitrary node — does not record. The escape hatch; prefer the checked steps. */ @RiskyNavigation @OptIn(RiskyNavigation::class) - fun , MNEXT> jumpTo(node: M): PathWalk = - PathWalk(path.jumpTo(node)) + fun , MNEXT> jumpTo(node: M): PathWalk = PathWalk(path.jumpTo(node)) /** * The nodes recorded so far, in walk order. diff --git a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPath.kt b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPath.kt index 50177a92..4fc95393 100644 --- a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPath.kt +++ b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPath.kt @@ -40,8 +40,6 @@ class ProcessPath internal constructor( * Starts a path at [start], recording it as the first node. */ @JvmStatic - fun from(start: N): ProcessPath { - return ProcessPath(current = start, recorded = listOf(start)) - } + fun from(start: N): ProcessPath = ProcessPath(current = start, recorded = listOf(start)) } } diff --git a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPathSteps.kt b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPathSteps.kt index 970af2a7..cda9289f 100644 --- a/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPathSteps.kt +++ b/bpmn-to-code-runtime/src/main/kotlin/io/miragon/bpmn/runtime/path/ProcessPathSteps.kt @@ -96,9 +96,7 @@ fun >> ProcessPath.inside( * (AND) branches whose relative order isn't defined. Feed the result to `hasPassed` / `hasNotPassed`. */ @SafeVarargs -fun nodesOf(vararg branches: List): List { - return branches.flatMap { it }.distinct() -} +fun nodesOf(vararg branches: List): List = branches.flatMap { it }.distinct() /** * Re-anchor to an arbitrary node **without** recording it and **without** checking adjacency — the last-resort @@ -106,6 +104,4 @@ fun nodesOf(vararg branches: List): List { * [RiskyNavigation] so every use is an explicit `@OptIn`; prefer the checked steps. */ @RiskyNavigation -fun ProcessPath<*>.jumpTo(node: M): ProcessPath { - return ProcessPath(current = node, recorded = nodes) -} +fun ProcessPath<*>.jumpTo(node: M): ProcessPath = ProcessPath(current = node, recorded = nodes) diff --git a/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/PathWalkKotlinApiTest.kt b/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/PathWalkKotlinApiTest.kt index 6cf9da28..05be44dd 100644 --- a/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/PathWalkKotlinApiTest.kt +++ b/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/PathWalkKotlinApiTest.kt @@ -1,8 +1,8 @@ package io.miragon.bpmn.runtime.path -import io.miragon.bpmn.runtime.path.example.NewsletterSubscriptionProcessApi.Relations as Newsletter import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import io.miragon.bpmn.runtime.path.example.NewsletterSubscriptionProcessApi.Relations as Newsletter /** * Exercises the fluent [PathWalk] facade over the generated Newsletter API from **Kotlin** (its diff --git a/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/ProcessPathKotlinApiTest.kt b/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/ProcessPathKotlinApiTest.kt index 8500e65b..8c2e37d2 100644 --- a/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/ProcessPathKotlinApiTest.kt +++ b/bpmn-to-code-runtime/src/test/kotlin/io/miragon/bpmn/runtime/path/ProcessPathKotlinApiTest.kt @@ -1,9 +1,9 @@ package io.miragon.bpmn.runtime.path -import io.miragon.bpmn.runtime.path.example.NewsletterSubscriptionProcessApi.Relations as Newsletter import io.miragon.bpmn.runtime.path.example.NewsletterSubscriptionProcessApi.Relations.SubProcessConfirmation import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import io.miragon.bpmn.runtime.path.example.NewsletterSubscriptionProcessApi.Relations as Newsletter /** * Exercises [ProcessPath] over the *actually generated* Kotlin Newsletter API — which doubles as the compile diff --git a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnResourceLoader.kt b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnResourceLoader.kt index b45761e5..521bb7da 100644 --- a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnResourceLoader.kt +++ b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnResourceLoader.kt @@ -37,12 +37,10 @@ internal object BpmnResourceLoader { return walkForBpmnFiles(directory) } - private fun loadFromUrl(url: URL): List { - return when (url.protocol) { - "file" -> loadFromPath(Path.of(url.toURI())) - "jar" -> loadFromJar(url) - else -> error("Unsupported classpath protocol: ${url.protocol}") - } + private fun loadFromUrl(url: URL): List = when (url.protocol) { + "file" -> loadFromPath(Path.of(url.toURI())) + "jar" -> loadFromJar(url) + else -> error("Unsupported classpath protocol: ${url.protocol}") } private fun loadFromPath(path: Path): List { @@ -75,10 +73,8 @@ internal object BpmnResourceLoader { } } - private fun walkForBpmnFiles(directory: Path): List { - return Files.walk(directory) - .filter { it.extension == "bpmn" } - .map { BpmnResource(fileName = it.name, content = it.readBytes()) } - .toList() - } + private fun walkForBpmnFiles(directory: Path): List = Files.walk(directory) + .filter { it.extension == "bpmn" } + .map { BpmnResource(fileName = it.name, content = it.readBytes()) } + .toList() } diff --git a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnRules.kt b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnRules.kt index cfe4b3a1..2ca8a4bb 100644 --- a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnRules.kt +++ b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnRules.kt @@ -164,19 +164,17 @@ object BpmnRules { * explicitly via [BpmnValidator.withRules]. */ @JvmStatic - fun all(): List { - return listOf( - MISSING_SERVICE_TASK_IMPLEMENTATION, - MISSING_MESSAGE_NAME, - MISSING_ERROR_DEFINITION, - MISSING_SIGNAL_NAME, - UNREFERENCED_ROOT_ELEMENT, - MISSING_TIMER_DEFINITION, - MISSING_CALLED_ELEMENT, - MISSING_ELEMENT_ID, - EMPTY_PROCESS, - MISSING_PROCESS_ID, - COLLISION_DETECTION, - ) - } + fun all(): List = listOf( + MISSING_SERVICE_TASK_IMPLEMENTATION, + MISSING_MESSAGE_NAME, + MISSING_ERROR_DEFINITION, + MISSING_SIGNAL_NAME, + UNREFERENCED_ROOT_ELEMENT, + MISSING_TIMER_DEFINITION, + MISSING_CALLED_ELEMENT, + MISSING_ELEMENT_ID, + EMPTY_PROCESS, + MISSING_PROCESS_ID, + COLLISION_DETECTION, + ) } diff --git a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidationAssert.kt b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidationAssert.kt index 41abcd21..32890728 100644 --- a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidationAssert.kt +++ b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidationAssert.kt @@ -131,9 +131,7 @@ class BpmnValidationAssert( /** * Returns the underlying [ValidationResult] for custom assertions. */ - fun result(): ValidationResult { - return actual - } + fun result(): ValidationResult = actual /** * Returns the violations for the given rule, optionally narrowed to a specific element. @@ -152,20 +150,16 @@ class BpmnValidationAssert( * Entry point for [BpmnValidationAssert]. */ @JvmStatic - fun assertThat(result: ValidationResult): BpmnValidationAssert { - return BpmnValidationAssert(result) - } + fun assertThat(result: ValidationResult): BpmnValidationAssert = BpmnValidationAssert(result) - private fun formatViolations(violations: List): String { - return violations.joinToString("\n") { violation -> - val severity = if (violation.severity == Severity.ERROR) "ERROR" else "WARN" - val location = if (violation.elementId != null) { - "${violation.processId}/${violation.elementId}" - } else { - violation.processId - } - "[$severity] $location: ${violation.message} (rule: ${violation.ruleId})" + private fun formatViolations(violations: List): String = violations.joinToString("\n") { violation -> + val severity = if (violation.severity == Severity.ERROR) "ERROR" else "WARN" + val location = if (violation.elementId != null) { + "${violation.processId}/${violation.elementId}" + } else { + violation.processId } + "[$severity] $location: ${violation.message} (rule: ${violation.ruleId})" } } } diff --git a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidator.kt b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidator.kt index 3bbf3425..8fece430 100644 --- a/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidator.kt +++ b/bpmn-to-code-testing/src/main/kotlin/io/miragon/bpmn/testing/BpmnValidator.kt @@ -93,12 +93,10 @@ class BpmnValidator private constructor( return BpmnValidationAssert.assertThat(result) } - private fun applyPolicy(violations: List): List { - return if (failOnWarning) { - violations.map { if (it.severity == Severity.WARN) it.copy(severity = Severity.ERROR) else it } - } else { - violations - } + private fun applyPolicy(violations: List): List = if (failOnWarning) { + violations.map { if (it.severity == Severity.WARN) it.copy(severity = Severity.ERROR) else it } + } else { + violations } private fun resolveRules(): List { @@ -145,16 +143,12 @@ class BpmnValidator private constructor( * Loads BPMN files from the classpath at the given path. */ @JvmStatic - fun fromClasspath(path: String): BpmnValidator { - return BpmnValidator { BpmnResourceLoader.fromClasspath(path) } - } + fun fromClasspath(path: String): BpmnValidator = BpmnValidator { BpmnResourceLoader.fromClasspath(path) } /** * Loads BPMN files from a filesystem directory. */ @JvmStatic - fun fromDirectory(directory: Path): BpmnValidator { - return BpmnValidator { BpmnResourceLoader.fromDirectory(directory) } - } + fun fromDirectory(directory: Path): BpmnValidator = BpmnValidator { BpmnResourceLoader.fromDirectory(directory) } } } diff --git a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnProcessArchitectureTest.kt b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnProcessArchitectureTest.kt index b586b843..4a668fd3 100644 --- a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnProcessArchitectureTest.kt +++ b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnProcessArchitectureTest.kt @@ -23,22 +23,20 @@ class BpmnProcessArchitectureTest { override val severity = Severity.WARN override val phase = ValidationPhase.PRE_MERGE - override fun validate(context: SingleModelValidationContext): List { - return context.model.serviceTasks - .filter { task -> - val name = task.id ?: "" - !name.startsWith("Activity_") && !name.startsWith("Task_") - } - .map { task -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = task.id, - processId = context.model.processId, - message = "Service task '${task.id}' should start with 'Activity_' or 'Task_'", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.serviceTasks + .filter { task -> + val name = task.id ?: "" + !name.startsWith("Activity_") && !name.startsWith("Task_") + } + .map { task -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = task.id, + processId = context.model.processId, + message = "Service task '${task.id}' should start with 'Activity_' or 'Task_'", + ) + } } @Test diff --git a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnResourceLoaderTest.kt b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnResourceLoaderTest.kt index 8978eced..c61eb211 100644 --- a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnResourceLoaderTest.kt +++ b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnResourceLoaderTest.kt @@ -1,15 +1,15 @@ package io.miragon.bpmn.testing +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir import java.io.FileOutputStream import java.net.URLClassLoader import java.nio.file.Files import java.nio.file.Path import java.util.jar.JarEntry import java.util.jar.JarOutputStream -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.io.TempDir class BpmnResourceLoaderTest { @@ -35,7 +35,6 @@ class BpmnResourceLoaderTest { @Test fun `fromDirectory loads all bpmn files recursively`(@TempDir tempDir: Path) { - // given: a directory with BPMN files in root and subdirectory, plus a non-BPMN file val subDir = Files.createDirectory(tempDir.resolve("sub")) Files.createFile(tempDir.resolve("root.bpmn")) @@ -51,7 +50,6 @@ class BpmnResourceLoaderTest { @Test fun `fromDirectory throws when path is not a directory`(@TempDir tempDir: Path) { - // given: a regular file (not a directory) val file = Files.createFile(tempDir.resolve("process.bpmn")) @@ -70,7 +68,6 @@ class BpmnResourceLoaderTest { @Test fun `fromClasspath loads bpmn files from jar archive`(@TempDir tempDir: Path) { - // given: a JAR containing a .bpmn file val jarPath = tempDir.resolve("test-resources.jar") JarOutputStream(FileOutputStream(jarPath.toFile())).use { jar -> diff --git a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnValidatorTest.kt b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnValidatorTest.kt index d8ba40ee..72bf6d66 100644 --- a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnValidatorTest.kt +++ b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/BpmnValidatorTest.kt @@ -5,11 +5,11 @@ import io.miragon.bpmn.domain.validation.SingleModelValidationRule import io.miragon.bpmn.domain.validation.model.Severity import io.miragon.bpmn.domain.validation.model.SingleModelValidationContext import io.miragon.bpmn.domain.validation.model.ValidationViolation -import java.nio.file.Files -import java.nio.file.Path import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir +import java.nio.file.Files +import java.nio.file.Path class BpmnValidatorTest { @@ -77,7 +77,6 @@ class BpmnValidatorTest { @Test fun `fromDirectory loads bpmn files`(@TempDir tempDir: Path) { - // given: a BPMN file copied into a temp directory val bpmnContent = javaClass.classLoader.getResourceAsStream("bpmn/valid-process.bpmn")!! Files.copy(bpmnContent, tempDir.resolve("test.bpmn")) @@ -105,16 +104,14 @@ class BpmnValidatorTest { override val severity = Severity.ERROR override val mandatory = true - override fun validate(context: SingleModelValidationContext): List { - return listOf( - ValidationViolation( - ruleId = id, - severity = severity, - elementId = null, - processId = context.model.processId, - message = "always fails", - ), - ) - } + override fun validate(context: SingleModelValidationContext): List = listOf( + ValidationViolation( + ruleId = id, + severity = severity, + elementId = null, + processId = context.model.processId, + message = "always fails", + ), + ) } } diff --git a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/SingleModelRuleTest.kt b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/SingleModelRuleTest.kt index 6fdfcf6a..cd5e3ed9 100644 --- a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/SingleModelRuleTest.kt +++ b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/SingleModelRuleTest.kt @@ -89,18 +89,16 @@ class SingleModelRuleTest { override val id = "call-activity-required-inputs" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.callActivities.flatMap { callActivity -> - val declaredTargets = callActivity.inputMappings.mapNotNull { it.target }.toSet() - (required - declaredTargets).map { missing -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = callActivity.id, - processId = context.model.processId, - message = "Call activity '${callActivity.id}' must pass input variable '$missing' to the called process.", - ) - } + override fun validate(context: SingleModelValidationContext): List = context.model.callActivities.flatMap { callActivity -> + val declaredTargets = callActivity.inputMappings.mapNotNull { it.target }.toSet() + (required - declaredTargets).map { missing -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = callActivity.id, + processId = context.model.processId, + message = "Call activity '${callActivity.id}' must pass input variable '$missing' to the called process.", + ) } } } @@ -109,18 +107,16 @@ class SingleModelRuleTest { override val id = "call-activity-required-outputs" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return context.model.callActivities.flatMap { callActivity -> - val declaredTargets = callActivity.outputMappings.mapNotNull { it.target }.toSet() - (required - declaredTargets).map { missing -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = callActivity.id, - processId = context.model.processId, - message = "Call activity '${callActivity.id}' must return output variable '$missing' to the parent process.", - ) - } + override fun validate(context: SingleModelValidationContext): List = context.model.callActivities.flatMap { callActivity -> + val declaredTargets = callActivity.outputMappings.mapNotNull { it.target }.toSet() + (required - declaredTargets).map { missing -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = callActivity.id, + processId = context.model.processId, + message = "Call activity '${callActivity.id}' must return output variable '$missing' to the parent process.", + ) } } } @@ -133,23 +129,21 @@ class SingleModelRuleTest { override val severity = Severity.ERROR private val allowed = Regex("""\$\{(null|true|false|execution\.getVariable\('[^']+'\))}""") - override fun validate(context: SingleModelValidationContext): List { - return context.model.flowNodes - .flatMap { node -> node.variables.map { node to it } } - .filter { (_, variable) -> variable.direction == VariableDirection.OUTPUT } - .filter { (_, variable) -> - val expression = variable.valueExpression - expression != null && !allowed.matches(expression) - } - .map { (node, variable) -> - ValidationViolation( - ruleId = id, - severity = severity, - elementId = node.id, - processId = context.model.processId, - message = "Output expression '${variable.valueExpression}' is not allowed.", - ) - } - } + override fun validate(context: SingleModelValidationContext): List = context.model.flowNodes + .flatMap { node -> node.variables.map { node to it } } + .filter { (_, variable) -> variable.direction == VariableDirection.OUTPUT } + .filter { (_, variable) -> + val expression = variable.valueExpression + expression != null && !allowed.matches(expression) + } + .map { (node, variable) -> + ValidationViolation( + ruleId = id, + severity = severity, + elementId = node.id, + processId = context.model.processId, + message = "Output expression '${variable.valueExpression}' is not allowed.", + ) + } } } diff --git a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/ValidationPhaseTest.kt b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/ValidationPhaseTest.kt index 48d7f91d..061ec111 100644 --- a/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/ValidationPhaseTest.kt +++ b/bpmn-to-code-testing/src/test/kotlin/io/miragon/bpmn/testing/ValidationPhaseTest.kt @@ -47,17 +47,15 @@ class ValidationPhaseTest { override val id = "always-failing" override val severity = Severity.ERROR - override fun validate(context: SingleModelValidationContext): List { - return listOf( - ValidationViolation( - ruleId = id, - severity = severity, - elementId = null, - processId = context.model.processId, - message = "always fails", - ), - ) - } + override fun validate(context: SingleModelValidationContext): List = listOf( + ValidationViolation( + ruleId = id, + severity = severity, + elementId = null, + processId = context.model.processId, + message = "always fails", + ), + ) } private class RecordingCrossModelRule : CrossModelValidationRule { diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/Application.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/Application.kt index faf6eebb..26cf75e1 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/Application.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/Application.kt @@ -30,27 +30,29 @@ import kotlinx.serialization.json.Json private val logger = KotlinLogging.logger {} fun main() { - val appConfig = AppConfig.fromEnvironment() embeddedServer( factory = Netty, port = appConfig.port, host = "0.0.0.0", - module = { configureApp(appConfig) } + module = { configureApp(appConfig) }, ).start( - wait = true + wait = true, ) } @Suppress("LongMethod") fun Application.configureApp( - appConfig: AppConfig + appConfig: AppConfig, ) { - // JSON serialization install(ContentNegotiation) { - val jsonSettings = Json { prettyPrint = true; isLenient = true; ignoreUnknownKeys = true } + val jsonSettings = Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + } json(jsonSettings) } @@ -67,7 +69,7 @@ fun Application.configureApp( appConfig.cors.allowedOrigins.forEach { origin -> allowHost( host = origin.removePrefix("https://").removePrefix("http://"), - schemes = listOf("https", "http") + schemes = listOf("https", "http"), ) } } diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/AppConfig.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/AppConfig.kt index 64ea02db..066a6fa6 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/AppConfig.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/AppConfig.kt @@ -27,5 +27,4 @@ data class AppConfig( return props.getProperty("version", "unknown") } } - } diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/CorsConfig.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/CorsConfig.kt index 1a382aac..aa35e5ec 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/CorsConfig.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/CorsConfig.kt @@ -4,7 +4,7 @@ package io.miragon.bpmn.web.config * CORS configuration loaded from environment variables */ data class CorsConfig( - val allowedOrigins: List + val allowedOrigins: List, ) { companion object { /** diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/LegalLinksConfig.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/LegalLinksConfig.kt index 5a316470..1c74e05b 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/LegalLinksConfig.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/config/LegalLinksConfig.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable @Serializable data class LegalLinksConfig( val imprintUrl: String?, - val privacyUrl: String? + val privacyUrl: String?, ) { companion object { fun fromEnvironment(): LegalLinksConfig { @@ -14,4 +14,4 @@ data class LegalLinksConfig( return LegalLinksConfig(imprintUrl = imprintUrl, privacyUrl = privacyUrl) } } -} \ No newline at end of file +} diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateRequest.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateRequest.kt index 263390d9..748c83c5 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateRequest.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateRequest.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable @Serializable data class GenerateRequest( val files: List, - val config: GenerationConfig + val config: GenerationConfig, ) { @Serializable @@ -20,12 +20,12 @@ data class GenerateRequest( /** * The BPMN XML encoded in Base64. */ - val content: String + val content: String, ) @Serializable data class GenerationConfig( val outputLanguage: OutputLanguage, - val processEngine: ProcessEngine + val processEngine: ProcessEngine, ) } diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateResponse.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateResponse.kt index 503aeb3b..bdc3ba68 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateResponse.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/model/GenerateResponse.kt @@ -19,7 +19,7 @@ data class GenerateResponse( data class GeneratedFile( val fileName: String, val content: String, - val processId: String + val processId: String, ) @Serializable @@ -35,29 +35,29 @@ data class GenerateResponse( fun noFilesProvided() = GenerateResponse( success = false, files = emptyList(), - error = "No files provided" + error = "No files provided", ) fun tooManyFiles() = GenerateResponse( success = false, files = emptyList(), - error = "Maximum 3 BPMN files allowed" + error = "Maximum 3 BPMN files allowed", ) fun unknownError() = GenerateResponse( success = false, files = emptyList(), error = "Unknown error occurred", - statusCode = HttpStatusCode.InternalServerError + statusCode = HttpStatusCode.InternalServerError, ) fun fromValidationException( - exception: BpmnValidationException + exception: BpmnValidationException, ) = GenerateResponse( success = false, files = emptyList(), error = exception.message, - statusCode = HttpStatusCode.BadRequest + statusCode = HttpStatusCode.BadRequest, ) } } diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateJsonRoutes.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateJsonRoutes.kt index bac44360..99c41a0f 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateJsonRoutes.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateJsonRoutes.kt @@ -15,9 +15,7 @@ import io.miragon.bpmn.web.service.WebJsonGenerationService fun Route.generateJsonRoutes( jsonService: WebJsonGenerationService, ) { - post("/api/generate-json") { - val request = call.receive() if (request.files.isEmpty()) { diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateRoutes.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateRoutes.kt index 8505ead8..cdf22c0d 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateRoutes.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/routes/GenerateRoutes.kt @@ -13,11 +13,9 @@ import io.miragon.bpmn.web.model.GenerateResponse import io.miragon.bpmn.web.service.WebGenerationService fun Route.generateRoutes( - generationService: WebGenerationService + generationService: WebGenerationService, ) { - post("/api/generate") { - val request = call.receive() if (request.files.isEmpty()) { diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/LibrarySourceProvider.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/LibrarySourceProvider.kt index 44994d9d..b79df663 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/LibrarySourceProvider.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/LibrarySourceProvider.kt @@ -14,9 +14,7 @@ class LibrarySourceProvider { private val cache: List by lazy { loadLibraryFiles() } private val version: String by lazy { loadProjectVersion() } - fun libraryFiles(): List { - return cache - } + fun libraryFiles(): List = cache fun runtimeDependency(): GenerateResponse.RuntimeDependency { val group = "io.miragon" diff --git a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/WebGenerationService.kt b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/WebGenerationService.kt index 7557431d..8e8552d3 100644 --- a/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/WebGenerationService.kt +++ b/bpmn-to-code-web/src/main/kotlin/io/miragon/bpmn/web/service/WebGenerationService.kt @@ -48,7 +48,7 @@ class WebGenerationService( bpmnContents = bpmnContents, packagePath = "com.example.process", outputLanguage = config.outputLanguage, - engine = config.processEngine + engine = config.processEngine, ) private fun buildCommand(file: GenerateRequest.BpmnFileData): CreateProcessApiInMemoryPlugin.BpmnInput { @@ -56,12 +56,12 @@ class WebGenerationService( val processName = file.fileName.removeSuffix(".bpmn") return CreateProcessApiInMemoryPlugin.BpmnInput( bpmnXml = bpmnXml, - processName = processName + processName = processName, ) } private fun mapToResponse( - apiFile: GeneratedApiFile + apiFile: GeneratedApiFile, ) = GenerateResponse.GeneratedFile( fileName = apiFile.fileName, content = apiFile.content, diff --git a/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebGenerationServiceTest.kt b/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebGenerationServiceTest.kt index 3a13281d..45f07ddd 100644 --- a/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebGenerationServiceTest.kt +++ b/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebGenerationServiceTest.kt @@ -3,9 +3,9 @@ package io.miragon.bpmn.web.service import io.miragon.bpmn.domain.shared.OutputLanguage import io.miragon.bpmn.domain.shared.ProcessEngine import io.miragon.bpmn.web.model.GenerateRequest -import java.util.Base64 import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import java.util.Base64 class WebGenerationServiceTest { @@ -13,19 +13,18 @@ class WebGenerationServiceTest { @Test fun `should generate Kotlin API from BPMN file`() { - // given: a valid Zeebe BPMN file encoded as Base64 val request = GenerateRequest( files = listOf( GenerateRequest.BpmnFileData( fileName = "c8-subscribe-newsletter.bpmn", content = loadBpmnBase64("bpmn/c8-subscribe-newsletter.bpmn"), - ) + ), ), config = GenerateRequest.GenerationConfig( outputLanguage = OutputLanguage.KOTLIN, processEngine = ProcessEngine.ZEEBE, - ) + ), ) // when: generating the API @@ -43,19 +42,18 @@ class WebGenerationServiceTest { @Test fun `should generate Java API from BPMN file`() { - // given: a valid Zeebe BPMN file with Java output language val request = GenerateRequest( files = listOf( GenerateRequest.BpmnFileData( fileName = "c8-subscribe-newsletter.bpmn", content = loadBpmnBase64("bpmn/c8-subscribe-newsletter.bpmn"), - ) + ), ), config = GenerateRequest.GenerationConfig( outputLanguage = OutputLanguage.JAVA, processEngine = ProcessEngine.ZEEBE, - ) + ), ) // when: generating the API @@ -71,19 +69,18 @@ class WebGenerationServiceTest { @Test fun `should reject a model whose target engine does not match the selected engine`() { - // given: a Zeebe model but Camunda 7 selected (the demo's original failure mode) val request = GenerateRequest( files = listOf( GenerateRequest.BpmnFileData( fileName = "c8-subscribe-newsletter.bpmn", content = loadBpmnBase64("bpmn/c8-subscribe-newsletter.bpmn"), - ) + ), ), config = GenerateRequest.GenerationConfig( outputLanguage = OutputLanguage.KOTLIN, processEngine = ProcessEngine.CAMUNDA_7, - ) + ), ) // when: generating the API @@ -98,19 +95,18 @@ class WebGenerationServiceTest { @Test fun `should reject a Camunda 7 model when Operaton is selected`() { - // given: a Camunda 7 model but Operaton selected (the reported case) val request = GenerateRequest( files = listOf( GenerateRequest.BpmnFileData( fileName = "c7-subscribe-newsletter.bpmn", content = loadBpmnBase64("bpmn/c7-subscribe-newsletter.bpmn"), - ) + ), ), config = GenerateRequest.GenerationConfig( outputLanguage = OutputLanguage.KOTLIN, processEngine = ProcessEngine.OPERATON, - ) + ), ) // when: generating the API @@ -123,19 +119,18 @@ class WebGenerationServiceTest { @Test fun `should handle invalid Base64 content gracefully`() { - // given: a request with invalid Base64 content val request = GenerateRequest( files = listOf( GenerateRequest.BpmnFileData( fileName = "invalid.bpmn", content = "not-valid-base64!!!", - ) + ), ), config = GenerateRequest.GenerationConfig( outputLanguage = OutputLanguage.KOTLIN, processEngine = ProcessEngine.ZEEBE, - ) + ), ) // when: generating the API @@ -149,7 +144,6 @@ class WebGenerationServiceTest { @Test fun `should process up to 3 BPMN files successfully`() { - // given: a request with 3 identical BPMN files val c8Base64 = loadBpmnBase64("bpmn/c8-subscribe-newsletter.bpmn") val request = GenerateRequest( @@ -161,7 +155,7 @@ class WebGenerationServiceTest { config = GenerateRequest.GenerationConfig( outputLanguage = OutputLanguage.KOTLIN, processEngine = ProcessEngine.ZEEBE, - ) + ), ) // when: generating the API for all files diff --git a/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebJsonGenerationServiceTest.kt b/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebJsonGenerationServiceTest.kt index 5a0f4c80..bd310a14 100644 --- a/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebJsonGenerationServiceTest.kt +++ b/bpmn-to-code-web/src/test/kotlin/io/miragon/bpmn/web/service/WebJsonGenerationServiceTest.kt @@ -2,9 +2,9 @@ package io.miragon.bpmn.web.service import io.miragon.bpmn.domain.shared.ProcessEngine import io.miragon.bpmn.web.model.GenerateJsonRequest -import java.util.Base64 import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import java.util.Base64 class WebJsonGenerationServiceTest { @@ -12,18 +12,17 @@ class WebJsonGenerationServiceTest { @Test fun `should generate JSON from sample BPMN file`() { - // given: the sample BPMN served by the web app val request = GenerateJsonRequest( files = listOf( GenerateJsonRequest.BpmnFileData( fileName = "c8-newsletter.bpmn", content = loadSampleBase64("samples/c8-newsletter.bpmn"), - ) + ), ), config = GenerateJsonRequest.JsonGenerationConfig( processEngine = ProcessEngine.ZEEBE, - ) + ), ) // when: generating JSON @@ -40,18 +39,17 @@ class WebJsonGenerationServiceTest { @Test fun `should return error response when base64 content is invalid`() { - // given: a request with invalid Base64 content val request = GenerateJsonRequest( files = listOf( GenerateJsonRequest.BpmnFileData( fileName = "invalid.bpmn", content = "not-valid-base64!!!", - ) + ), ), config = GenerateJsonRequest.JsonGenerationConfig( processEngine = ProcessEngine.ZEEBE, - ) + ), ) // when: generating JSON diff --git a/build.gradle.kts b/build.gradle.kts index 39089ccd..99701005 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,6 +8,7 @@ plugins { alias(libs.plugins.kotlin.jvm) alias(libs.plugins.mavenPublish) apply false alias(libs.plugins.detekt) apply false + alias(libs.plugins.ktlint) apply false } allprojects { @@ -20,12 +21,17 @@ allprojects { subprojects { apply(plugin = "io.gitlab.arturbosch.detekt") + apply(plugin = "io.github.usefulness.ktlint-gradle-plugin") configure { config.setFrom("$rootDir/config/detekt/detekt.yml") buildUponDefaultConfig = true } + tasks.matching { it.name == "check" }.configureEach { + dependsOn("detekt") + } + tasks.withType().configureEach { compilerOptions { jvmTarget = JvmTarget.JVM_21 @@ -37,8 +43,6 @@ subprojects { targetCompatibility = "21" } - // Common JaCoCo configuration. Each module declares the jacoco plugin in its own - // plugins {} block to ensure classDirectories is properly wired to compileKotlin task outputs. plugins.withId("jacoco") { tasks.withType().configureEach { finalizedBy(tasks.named("jacocoTestReport")) @@ -53,8 +57,6 @@ subprojects { } tasks.withType().configureEach { - // Explicit dependency on compilation so Gradle's implicit-dependency - // validation passes when classDirectories is rebuilt from compiled output. dependsOn(tasks.withType()) violationRules { rule { diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index bdfa24ee..f6c6b47c 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -74,13 +74,11 @@ style: active: true max: 4 UnusedImports: - active: true + active: false UnusedPrivateMember: active: true WildcardImport: - active: true - excludeImports: - - 'io.ktor.*' + active: false ForbiddenComment: active: true comments: diff --git a/docs/contributing/index.md b/docs/contributing/index.md index aa3347bc..5e4b9791 100644 --- a/docs/contributing/index.md +++ b/docs/contributing/index.md @@ -20,16 +20,27 @@ brew install lefthook lefthook install ``` -This installs a `pre-push` hook that runs `:bpmn-to-code-core:jacocoTestCoverageVerification` — the same check enforced in CI. +This installs a `pre-push` hook that runs the same quality gate CI enforces: `compileKotlin`, +`lintKotlin` (ktlint), `detekt`, and `:bpmn-to-code-core:jacocoTestCoverageVerification`. ## Common Commands ```bash -./gradlew build # full build +./gradlew build # full build (compile + ktlint + detekt + tests) +./gradlew check # ktlint + detekt + tests +./gradlew lintKotlin # ktlint formatting check +./gradlew formatKotlin # ktlint auto-fix +./gradlew detekt # detekt static analysis ./gradlew :bpmn-to-code-core:test # run core tests only ./gradlew :bpmn-to-code-core:jacocoTestCoverageVerification # check coverage manually ``` +ktlint owns formatting/imports (config in `.editorconfig`); detekt owns semantic/structural +analysis (config in `config/detekt/detekt.yml`). Both are wired into `check`/`build` and gate CI +plus the pre-push hook — no baseline, no silent suppressions. The only scoped exceptions are the +ktor wildcard-import allowance and no hard line-length limit (`.editorconfig`) and the generated +runtime fixture (excluded in both `.editorconfig` and `bpmn-to-code-runtime/build.gradle.kts`). + ## Skipping hooks ```bash diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bec3073e..5cc3ed7d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,6 +20,7 @@ konsist = "0.17.3" jsonSchemaValidator = "1.5.6" dokka = "2.2.0" detekt = "1.23.8" +ktlintPlugin = "0.14.0" [libraries] # Plugin dependencies @@ -71,3 +72,4 @@ ktor = { id = "io.ktor.plugin", version.ref = "ktor" } shadow = { id = "com.gradleup.shadow", version.ref = "shadow" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +ktlint = { id = "io.github.usefulness.ktlint-gradle-plugin", version.ref = "ktlintPlugin" } diff --git a/lefthook.yml b/lefthook.yml index 680143e6..3b93aa84 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -6,6 +6,9 @@ pre-push: coverage: glob: "*.{kt,kts}" run: ./gradlew :bpmn-to-code-core:jacocoTestCoverageVerification + ktlint: + glob: "*.{kt,kts}" + run: ./gradlew lintKotlin detekt: glob: "*.{kt,kts}" run: ./gradlew detekt