diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index cb9eb89..f0dd18d 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -43,7 +43,7 @@ jobs: # Build - name: Build with Maven - run: ./mvnw clean verify -U -B -ntp -T4 + run: ./mvnw clean install -U -B -ntp -T4 # itest - name: Run itest diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 8db1cb0..57a8df0 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -34,7 +34,7 @@ jobs: # Build - name: Build with Maven - run: ./mvnw clean verify -U -B -T4 + run: ./mvnw clean install -U -B -T4 # Get GPG private key into GPG - name: Import GPG Owner Trust diff --git a/docs/process-engine-worker.md b/docs/process-engine-worker.md index 3148e7c..8367e04 100644 --- a/docs/process-engine-worker.md +++ b/docs/process-engine-worker.md @@ -75,8 +75,92 @@ If the annotation `@org.springframework.transaction.annotation.Transactional` wi is used, the library will execute the worker method and the completion of the external task via API in the same transaction. This will lead to a transaction rollback, if the external task can't be completed (e.g. due to a network error). +## Documentation +You can automatically document your workers with the `process-engine-worker-documentation-api` and create on each build with the `process-engine-worker-documentation-maven-plugin` maven plugin element templates for the workers. +1. Add the `process-engine-worker-documentation-api` +```xml + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-api + ${process-engine-worker.version} + +``` + +2. Add the `@ProcessEngineWorkerDocumentation` annotation to your worker + +```kotlin + @ProcessEngineWorker("example-worker") + @ProcessEngineWorkerDocumentation(name = "Example Worker", description = "Example worker for documentation generation") + fun execute(@Variable(name = "exampleDto") exampleDto: ExampleDto): ExampleDto { + return exampleDto + } +``` + +3. Add the `@ProcessEngineWorkerPropertyDocumentation` annotation to your in and output variables + +```kotlin +data class ExampleDto( + @field:ProcessEngineWorkerPropertyDocumentation(label = "Example Input") + val exampleInput: String +) { +} +``` +```java +public record ExampleDto( + @ProcessEngineWorkerPropertyDocumentation(label = "Example Input") + private String exampleInput +){} +``` + +4. Add the maven plugin to your `pom.xml`s plugin section + +**C7 Maven Plugin** + +```xml + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-c7-maven-plugin + 0.6.1-SNAPSHOT + + true + src/main/resources/bpmn/element-templates + + true + + + + + + generate + + process-classes + + + +``` + +**C8 Maven Plugin** +```xml + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-c8-maven-plugin + 0.6.1-SNAPSHOT + + true + src/main/resources/bpmn/element-templates + + + + + generate + + process-classes + + + +``` diff --git a/documentation/documentation-api/pom.xml b/documentation/documentation-api/pom.xml new file mode 100644 index 0000000..53c3d9e --- /dev/null +++ b/documentation/documentation-api/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation + 0.6.1-SNAPSHOT + + + process-engine-worker-documentation-api + ${project.artifactId} + + diff --git a/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/ProcessEngineWorkerDocumentation.kt b/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/ProcessEngineWorkerDocumentation.kt new file mode 100644 index 0000000..212e3b0 --- /dev/null +++ b/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/ProcessEngineWorkerDocumentation.kt @@ -0,0 +1,7 @@ +package dev.bpmcrafters.processengine.worker.documentation.api + +annotation class ProcessEngineWorkerDocumentation( + val name: String, + val description: String = "", + val version: Int = -1 +) diff --git a/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/ProcessEngineWorkerPropertyDocumentation.kt b/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/ProcessEngineWorkerPropertyDocumentation.kt new file mode 100644 index 0000000..3e4fdd8 --- /dev/null +++ b/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/ProcessEngineWorkerPropertyDocumentation.kt @@ -0,0 +1,8 @@ +package dev.bpmcrafters.processengine.worker.documentation.api + +annotation class ProcessEngineWorkerPropertyDocumentation( + val type: PropertyType = PropertyType.STRING, + val label: String, + val notEmpty: Boolean = false, + val editable: Boolean = true, +) diff --git a/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/PropertyType.kt b/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/PropertyType.kt new file mode 100644 index 0000000..0f82111 --- /dev/null +++ b/documentation/documentation-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/api/PropertyType.kt @@ -0,0 +1,7 @@ +package dev.bpmcrafters.processengine.worker.documentation.api + +enum class PropertyType(val type: String) { + STRING("String"), + TEXT("Text"), + HIDDEN("Hidden") +} diff --git a/documentation/documentation-c7-maven-plugin/pom.xml b/documentation/documentation-c7-maven-plugin/pom.xml new file mode 100644 index 0000000..b7fd0f4 --- /dev/null +++ b/documentation/documentation-c7-maven-plugin/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation + 0.6.1-SNAPSHOT + + + process-engine-worker-documentation-c7-maven-plugin + ${project.artifactId} + maven-plugin + Maven plugin to generate camunda 7 element templates + + + 3.9.11 + 3.15.1 + 0.10.2 + 3.15.1 + + + + + + org.apache.maven + maven-plugin-api + ${org.apache.maven.version} + + + + org.apache.maven + maven-core + ${org.apache.maven.version} + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${org.apache.maven.plugin-tools.version} + provided + + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${org.apache.maven-plugins.version} + + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${org.apache.maven-plugins.version} + + + + + + + org.jsonschema2pojo + jsonschema2pojo-maven-plugin + 1.2.2 + + ${basedir}/src/main/resources/schema + dev.bpmcrafters.processengine.worker.documentation.elementtemplates.gen + true + true + + + + generate-sources + + generate + + + + + + + + diff --git a/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/C7Config.kt b/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/C7Config.kt new file mode 100644 index 0000000..539f245 --- /dev/null +++ b/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/C7Config.kt @@ -0,0 +1,6 @@ +package dev.bpmcrafters.processengine.worker.documentation + +data class C7Config( + val asyncBeforeDefaultValue: Boolean = false, + val asyncAfterDefaultValue: Boolean = false +) diff --git a/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojo.kt b/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojo.kt new file mode 100644 index 0000000..76b6a75 --- /dev/null +++ b/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojo.kt @@ -0,0 +1,80 @@ +package dev.bpmcrafters.processengine.worker.documentation + +import dev.bpmcrafters.processengine.worker.documentation.core.WorkerDocumentationGenerator +import dev.bpmcrafters.processengine.worker.documentation.engine.Camunda7ElementTemplateGenerator +import dev.bpmcrafters.processengine.worker.documentation.generator.api.DocumentationFailedException +import dev.bpmcrafters.processengine.worker.documentation.generator.api.EngineDocumentationGeneratorApi +import dev.bpmcrafters.processengine.worker.documentation.generator.api.InputValueNamingPolicy +import org.apache.maven.plugin.AbstractMojo +import org.apache.maven.plugin.MojoExecutionException +import org.apache.maven.plugin.logging.Log +import org.apache.maven.plugins.annotations.LifecyclePhase +import org.apache.maven.plugins.annotations.Mojo +import org.apache.maven.plugins.annotations.Parameter +import org.apache.maven.plugins.annotations.ResolutionScope +import org.apache.maven.project.MavenProject +import java.io.File + +@Mojo(name = "generate", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.RUNTIME) +class WorkerDocumentationGeneratorMojo: AbstractMojo() { + + private val log: Log = getLog() + + @Parameter(defaultValue = "\${project}", required = true, readonly = true) + var project: MavenProject? = null + + /** + * Directory to output the generated element templates to. + */ + @Parameter(property = "elementtemplategen.outputDirectory", defaultValue = "\${project.build.directory/generated-sources/element-templates}") + var outputDirectory: File? = null + + /** + * The naming policy for input values. + * EMPTY => ${} + * ATTRIBUTE_NAME => ${} + */ + @Parameter(name = "inputValueNamingPolicy", property = "elementtemplategen.inputValueNamingPolicy", defaultValue = "EMPTY") + var inputValueNamingPolicy: InputValueNamingPolicy? = null + + /** + * A flag indicating if the output directory should be cleaned before generation. + */ + @Parameter(name = "clean", property = "elementtemplategen.clean", defaultValue = "false") + var clean: Boolean = false + + /** + * Platform-specific configuration options + */ + @Parameter + var c7Config: C7Config = C7Config() + + // Minimal factory hook for testing/injection + var generatorFactory: (MavenProject, File, EngineDocumentationGeneratorApi) -> WorkerDocumentationGenerator = + { project, outputDir, generator -> WorkerDocumentationGenerator(project, outputDir, generator) } + + override fun execute() { + log.info("Generating element templates for ${project?.artifactId}") + + val workerDocumentationGenerator = generatorFactory( + requireNotNull(project), + requireNotNull(outputDirectory), + Camunda7ElementTemplateGenerator(c7Config = c7Config) + ) + + try { + if (clean) { + log.info("Cleaning output directory...") + workerDocumentationGenerator.clean() + } + log.info("Generate worker documentation") + workerDocumentationGenerator.generate() + } catch (e: DocumentationFailedException) { + log.error(e.message) + log.debug(e) + throw MojoExecutionException(e.message) + } + + } + +} diff --git a/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda7ElementTemplateGenerator.kt b/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda7ElementTemplateGenerator.kt new file mode 100644 index 0000000..3d823a6 --- /dev/null +++ b/documentation/documentation-c7-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda7ElementTemplateGenerator.kt @@ -0,0 +1,174 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine + +import dev.bpmcrafters.processengine.worker.documentation.C7Config +import dev.bpmcrafters.processengine.worker.documentation.api.PropertyType +import dev.bpmcrafters.processengine.worker.documentation.core.SchemaJsonConverter +import dev.bpmcrafters.processengine.worker.documentation.elementtemplates.gen.Binding +import dev.bpmcrafters.processengine.worker.documentation.elementtemplates.gen.CamundaC7ElementTemplate +import dev.bpmcrafters.processengine.worker.documentation.elementtemplates.gen.Constraints +import dev.bpmcrafters.processengine.worker.documentation.elementtemplates.gen.Property +import dev.bpmcrafters.processengine.worker.documentation.generator.api.* + +class Camunda7ElementTemplateGenerator( + private val inputValueNamingPolicy: InputValueNamingPolicy = InputValueNamingPolicy.EMPTY, + private val c7Config: C7Config = C7Config() +): EngineDocumentationGeneratorApi { + + override fun generate(processEngineWorkerDocumentationInfo: ProcessEngineWorkerDocumentationInfo): GenerationResult { + val elementTemplate = CamundaC7ElementTemplate() + .withName(processEngineWorkerDocumentationInfo.name) + .withId(processEngineWorkerDocumentationInfo.type) + .withAppliesTo(mutableListOf(BPMNElementType.BPMN_SERVICE_TASK.value)) + + if (processEngineWorkerDocumentationInfo.version > 0) { + elementTemplate.version = processEngineWorkerDocumentationInfo.version.toDouble() + } + + + // Add external task property + val implementationProperty = createExternalTaskProperty() + elementTemplate.properties.add(implementationProperty) + + + // Add property for the topic of the external task + val implementationTopicProperty = createExternalTaskTopicProperty(processEngineWorkerDocumentationInfo.type) + elementTemplate.properties.add(implementationTopicProperty) + + + // Add asyncBefore property + val asyncBeforeProperty = createAsyncBeforeProperty(c7Config.asyncBeforeDefaultValue) + elementTemplate.properties.add(asyncBeforeProperty) + + + // Add asyncAfter property + val asyncAfterProperty = createAsyncAfterProperty(c7Config.asyncAfterDefaultValue) + elementTemplate.properties.add(asyncAfterProperty) + + + // Add properties for input parameters + for (inputProperty in processEngineWorkerDocumentationInfo.inputProperties) { + val property = createInputParameterProp(inputProperty, inputValueNamingPolicy) + elementTemplate.properties.add(property) + } + + + // Add properties for output parameters + for (outputProperties in processEngineWorkerDocumentationInfo.outputProperties) { + val property = createOutputParameterProp(outputProperties) + elementTemplate.properties.add(property) + } + + val jsonSchema = "https://unpkg.com/@camunda/element-templates-json-schema@0.1.0/resources/schema.json" + val json = SchemaJsonConverter.toJsonString(jsonSchema, elementTemplate, CamundaC7ElementTemplate::class.java) + + return GenerationResult( + name = processEngineWorkerDocumentationInfo.name, + version = processEngineWorkerDocumentationInfo.version, + content = json, + fileName = processEngineWorkerDocumentationInfo.name.replace(" ", "-") + ".json", + engine = TargetPlattform.C7 + ) + } + + private fun createInputParameterProp( + info: ProcessEngineWorkerPropertyInfo, + inputValueNamingPolicy: InputValueNamingPolicy + ): Property { + val property = Property() + .withLabel("Input: ${info.label}") + .withValue( + when (inputValueNamingPolicy) { + InputValueNamingPolicy.EMPTY -> "\${}" + InputValueNamingPolicy.ATTRIBUTE_NAME -> "\${${info.name}}" + } + ) + .withType(info.type.type) + .withChoices(null) + .withBinding( + Binding() + .withType(Binding.Type.CAMUNDA_INPUT_PARAMETER) + .withName(info.name) + ) + property.constraints = Constraints() + .withNotEmpty(info.notEmpty) + property.editable = info.editable + + return property + } + + private fun createOutputParameterProp(info: ProcessEngineWorkerPropertyInfo): Property { + val property = Property() + .withLabel("Output: ${info.label}") + .withValue(info.name) + .withType(info.type.type) + .withChoices(null) + .withBinding( + Binding() + .withType(Binding.Type.CAMUNDA_OUTPUT_PARAMETER) + .withSource("\${${info.name}}") + ) + property.constraints = Constraints() + .withNotEmpty(info.notEmpty) + property.editable = info.editable + + return property + } + + private fun createExternalTaskProperty(): Property { + return Property() + .withLabel("Implementation Type") + .withType(PropertyType.STRING.type) + .withValue("external") + .withEditable(false) + // See Java comment: avoid empty list in JSON by setting choices to null + .withChoices(null) + .withBinding( + Binding() + .withType(Binding.Type.PROPERTY) + .withName("camunda:type") + ) + } + + private fun createExternalTaskTopicProperty(type: String): Property { + return Property() + .withLabel("Topic") + .withType(PropertyType.STRING.type) + .withValue(type) + .withEditable(false) + .withChoices(null) + .withBinding( + Binding() + .withType(Binding.Type.PROPERTY) + .withName("camunda:topic") + ) + } + + private fun createAsyncBeforeProperty(asyncBefore: Boolean): Property { + return Property() + .withLabel("Async Before") + .withType("Boolean") + .withValue(asyncBefore) + .withEditable(true) + .withChoices(null) + .withBinding( + Binding() + .withType(Binding.Type.PROPERTY) + .withName("camunda:asyncBefore") + ) + } + + private fun createAsyncAfterProperty(asyncAfter: Boolean): Property { + return Property() + .withLabel("Async After") + .withType("Boolean") + .withValue(asyncAfter) + .withEditable(true) + .withChoices(null) + .withBinding( + Binding() + .withType(Binding.Type.PROPERTY) + .withName("camunda:asyncAfter") + ) + } + +} diff --git a/documentation/documentation-c7-maven-plugin/src/main/resources/schema/camunda-c7-element-template.json b/documentation/documentation-c7-maven-plugin/src/main/resources/schema/camunda-c7-element-template.json new file mode 100644 index 0000000..538ec46 --- /dev/null +++ b/documentation/documentation-c7-maven-plugin/src/main/resources/schema/camunda-c7-element-template.json @@ -0,0 +1,540 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://camunda.org/schema/element-templates/1.0", + "type": "object", + "title": "Element Template Schema", + "description": "An element template definition", + "required": [ + "name", + "id", + "appliesTo", + "properties" + ], + "properties": { + "name": { + "$id": "#/name", + "type": "string", + "title": "element template name", + "description": "The name of the element template" + }, + "id": { + "$id": "#/id", + "type": "string", + "title": "element template id", + "description": "The identifier of the element template" + }, + "description": { + "$id": "#/description", + "type": "string", + "title": "element template description", + "description": "The description of the element template" + }, + "version": { + "$id": "#/version", + "type": "number", + "title": "element template version", + "description": "The version of the element template" + }, + "isDefault": { + "$id": "#/isDefault", + "type": "boolean", + "title": "element template is default", + "description": "Indicates whether the element template is a default template" + }, + "appliesTo": { + "$id": "#/appliesTo", + "type": "array", + "title": "element template applies to", + "description": "The definition for which element types the element template can be applied", + "default": [], + "items": { + "$id": "#/appliesTo/items", + "type": "string", + "pattern": "^(bpmn:)" + } + }, + "properties": { + "$id": "#/properties", + "type": "array", + "title": "element template properties", + "description": "The properties of the element template", + "default": [], + "items": { + "$id": "#/properties/property", + "type": "object", + "title": "element template property", + "description": "A property defined for the element template", + "default": {}, + "required": [ + "binding" + ], + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "Dropdown" + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "choices" + ], + "errorMessage": "must provide choices=[] with \"Dropdown\" type" + } + }, + { + "if": { + "properties": { + "binding": { + "properties": { + "type": { + "const": "property" + } + }, + "required": [ + "type" + ] + } + }, + "required": [ + "binding" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "String", + "Text", + "Hidden", + "Dropdown", + "Boolean" + ], + "errorMessage": "invalid property type ${0} for binding type \"property\"; must be any of { String, Text, Hidden, Dropdown, Boolean }" + } + } + } + }, + { + "if": { + "properties": { + "binding": { + "properties": { + "type": { + "const": "camunda:executionListener" + } + }, + "required": [ + "type" + ] + } + }, + "required": [ + "binding" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "Hidden" + ], + "errorMessage": "invalid property type ${0} for binding type \"camunda:executionListener\"; must be \"Hidden\"" + } + } + } + }, + { + "if": { + "properties": { + "binding": { + "properties": { + "type": { + "enum": [ + "camunda:property", + "camunda:outputParameter", + "camunda:in", + "camunda:in:businessKey", + "camunda:out" + ] + } + }, + "required": [ + "type" + ] + } + }, + "required": [ + "binding" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "String", + "Hidden", + "Dropdown" + ], + "errorMessage": "invalid property type ${0} for binding type ${1/binding/type}; must be any of { String, Hidden, Dropdown }" + } + } + } + }, + { + "if": { + "properties": { + "binding": { + "properties": { + "type": { + "enum": [ + "camunda:inputParameter", + "camunda:field" + ] + } + }, + "required": [ + "type" + ] + } + }, + "required": [ + "binding" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "String", + "Text", + "Hidden", + "Dropdown" + ], + "errorMessage": "invalid property type ${0} for binding type ${1/binding/type}; must be any of { String, Text, Hidden, Dropdown }" + } + } + } + } + ], + "properties": { + "value": { + "$id": "#/properties/property/value", + "type": [ + "string", + "boolean" + ], + "title": "property value", + "description": "The value of the control field for the property" + }, + "description": { + "$id": "#/properties/property/description", + "type": "string", + "title": "property description", + "description": "The description of the control field" + }, + "label": { + "$id": "#/properties/property/label", + "type": "string", + "title": "property label", + "description": "The label of the control field for the property" + }, + "type": { + "$id": "#/properties/property/type", + "type": "string", + "title": "property type", + "description": "The type of the control field" + }, + "editable": { + "$id": "#/properties/property/editable", + "type": "boolean", + "title": "property editable", + "description": "Indicates whether the property is editable or not" + }, + "choices": { + "$id": "#/properties/property/choices", + "type": "array", + "title": "property choices", + "description": "The choices for dropdown properties", + "items": { + "$id": "#/properties/property/choices/item", + "type": "object", + "properties": { + "name": { + "$id": "#/properties/property/choices/item/name", + "type": "string", + "title": "choice name", + "description": "The name of the choice" + }, + "value": { + "$id": "#/properties/property/choices/item/value", + "type": "string", + "title": "choice value", + "description": "The value of the choice" + } + }, + "required": [ + "value", + "name" + ], + "errorMessage": "{ name, value } must be specified for \"Dropdown\" choices" + } + }, + "binding": { + "$id": "#/properties/property/binding", + "type": "object", + "title": "property binding", + "description": "A binding to a BPMN 2.0 property", + "required": [ + "type" + ], + "allOf": [ + { + "if": { + "properties": { + "type": { + "enum": [ + "property", + "camunda:property", + "camunda:inputParameter", + "camunda:field" + ] + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "name" + ], + "errorMessage": "property.binding ${0/type} requires name" + } + }, + { + "if": { + "properties": { + "type": { + "const": "camunda:outputParameter" + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "source" + ], + "errorMessage": "property.binding ${0/type} requires source" + } + }, + { + "if": { + "properties": { + "type": { + "const": "camunda:in" + } + }, + "required": [ + "type" + ] + }, + "then": { + "oneOf": [ + { + "required": [ + "variables" + ] + }, + { + "required": [ + "target" + ] + } + ], + "errorMessage": "property.binding ${0/type} requires variables or target" + } + }, + { + "if": { + "properties": { + "type": { + "const": "camunda:out" + } + }, + "required": [ + "type" + ] + }, + "then": { + "oneOf": [ + { + "required": [ + "variables" + ] + }, + { + "required": [ + "source" + ] + }, + { + "required": [ + "sourceExpression" + ] + } + ], + "errorMessage": "property.binding ${0/type} requires variables, sourceExpression or source" + } + } + ], + "properties": { + "type": { + "$id": "#/properties/property/binding/type", + "type": "string", + "title": "property binding type", + "enum": [ + "property", + "camunda:property", + "camunda:inputParameter", + "camunda:outputParameter", + "camunda:in", + "camunda:out", + "camunda:in:businessKey", + "camunda:executionListener", + "camunda:field" + ], + "errorMessage": "invalid property.binding type ${0}; must be any of { property, camunda:property, camunda:inputParameter, camunda:outputParameter, camunda:in, camunda:out, camunda:in:businessKey, camunda:executionListener, camunda:field }", + "description": "The type of the property binding" + }, + "name": { + "$id": "#/properties/property/binding/name", + "type": "string", + "title": "property binding name", + "description": "The name of binding xml property" + }, + "event": { + "$id": "#/properties/property/binding/event", + "type": "string", + "title": "property binding event", + "description": "The event type of an execution listener binding" + }, + "scriptFormat": { + "$id": "#/properties/property/binding/scriptFormat", + "type": "string", + "title": "property binding script format", + "description": "The format of a script property binding (camunda:outputParameter, camunda:inputParameter)" + }, + "source": { + "$id": "#/properties/property/binding/source", + "type": "string", + "title": "property binding source", + "description": "The source value of a property binding (camunda:outputParameter, camunda:out)" + }, + "target": { + "$id": "#/properties/property/binding/target", + "type": "string", + "title": "property binding target", + "description": "The target value to be mapped to (camunda:in)" + }, + "expression": { + "$id": "#/properties/property/binding/expression", + "type": "boolean", + "title": "property binding expression", + "description": "True indicates that the control field value is an expression (camunda:in, camunda:field)" + }, + "variables": { + "$id": "#/properties/property/binding/variables", + "type": "string", + "title": "property binding variables", + "enum": [ + "all", + "local" + ], + "description": "Either all or local indicating the variable mapping (camunda:in)" + }, + "sourceExpression": { + "$id": "#/properties/property/binding/sourceExpression", + "type": "string", + "title": "property binding source expression", + "description": "The string containing the expression for the source attribute (camunda:out)" + } + } + }, + "constraints": { + "$id": "#/properties/property/constraints", + "type": "object", + "title": "property constraints", + "description": "The validation constraints", + "properties": { + "notEmpty": { + "$id": "#/properties/property/constraints/notEmpty", + "type": "boolean", + "title": "property constraints not empty", + "description": "The control field must not be empty" + }, + "minLength": { + "$id": "#/properties/property/constraints/minLength", + "type": "string", + "title": "property constraints min length", + "description": "The minimal length for the control field value" + }, + "maxLength": { + "$id": "#/properties/property/constraints/maxLength", + "type": "string", + "title": "property constraints max length", + "description": "The maximal length for the control field value" + }, + "pattern": { + "$id": "#/properties/property/constraints/pattern", + "type": "object", + "title": "property constraints pattern", + "description": "A regular expression pattern for the constraints", + "properties": { + "value": { + "$id": "#/properties/property/constraints/pattern/value", + "type": "string", + "title": "property constraints pattern value", + "description": "The regular expression of the pattern constraint" + }, + "message": { + "$id": "#/properties/property/constraints/pattern/message", + "type": "string", + "title": "property constraints pattern message", + "description": "The validation message of the pattern constraint" + } + } + } + } + } + } + } + }, + "metadata": { + "$id": "#/metadata", + "type": "object", + "title": "element template metadata", + "description": "Some metadata for further configuration" + }, + "scopes": { + "$id": "#/scopes", + "type": "object", + "title": "element template scope", + "description": "Special scoped bindings that allow you to configure nested elements" + }, + "entriesVisible": { + "$id": "#/entriesVisible", + "deprecated": true, + "type": "object", + "title": "element template entries visible", + "description": "@Deprecated - Select which entries are visible in the properties panel" + } + } +} \ No newline at end of file diff --git a/documentation/documentation-c7-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojoTest.kt b/documentation/documentation-c7-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojoTest.kt new file mode 100644 index 0000000..da1607a --- /dev/null +++ b/documentation/documentation-c7-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojoTest.kt @@ -0,0 +1,67 @@ +package dev.bpmcrafters.processengine.worker.documentation + +import dev.bpmcrafters.processengine.worker.documentation.core.WorkerDocumentationGenerator +import dev.bpmcrafters.processengine.worker.documentation.generator.api.DocumentationFailedException +import org.apache.maven.plugin.MojoExecutionException +import org.apache.maven.project.MavenProject +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import java.io.File + +class WorkerDocumentationGeneratorMojoTest { + + @Test + fun `execute with clean true calls clean and generate`() { + val mojo = WorkerDocumentationGeneratorMojo().apply { + project = mock() + outputDirectory = mock() + clean = true + } + + val mockGenerator = mock() + mojo.generatorFactory = { _, _, _ -> mockGenerator } + + mojo.execute() + + verify(mockGenerator).clean() + verify(mockGenerator).generate() + } + + @Test + fun `execute with clean false calls only generate`() { + val mojo = WorkerDocumentationGeneratorMojo().apply { + project = mock() + outputDirectory = mock() + clean = false + } + + val mockGenerator = mock() + mojo.generatorFactory = { _, _, _ -> mockGenerator } + + mojo.execute() + + verify(mockGenerator, never()).clean() + verify(mockGenerator).generate() + } + + @Test + fun `execute wraps DocumentationFailedException into MojoExecutionException`() { + val mojo = WorkerDocumentationGeneratorMojo().apply { + project = mock() + outputDirectory = mock() + clean = false + } + + val failingGenerator = mock { + on { generate() } doThrow DocumentationFailedException("boom", null) + } + mojo.generatorFactory = { _, _, _ -> failingGenerator } + + assertThatThrownBy { mojo.execute() } + .isInstanceOf(MojoExecutionException::class.java) + } +} diff --git a/documentation/documentation-c7-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda7ElementTemplateGeneratorTest.kt b/documentation/documentation-c7-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda7ElementTemplateGeneratorTest.kt new file mode 100644 index 0000000..21adff2 --- /dev/null +++ b/documentation/documentation-c7-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda7ElementTemplateGeneratorTest.kt @@ -0,0 +1,65 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine + +import dev.bpmcrafters.processengine.worker.documentation.C7Config +import dev.bpmcrafters.processengine.worker.documentation.api.PropertyType +import dev.bpmcrafters.processengine.worker.documentation.generator.api.InputValueNamingPolicy +import dev.bpmcrafters.processengine.worker.documentation.generator.api.ProcessEngineWorkerDocumentationInfo +import dev.bpmcrafters.processengine.worker.documentation.generator.api.ProcessEngineWorkerPropertyInfo +import dev.bpmcrafters.processengine.worker.documentation.generator.api.TargetPlattform +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class Camunda7ElementTemplateGeneratorTest { + + @Test + fun `generate builds expected JSON and metadata`() { + val config = C7Config(asyncBeforeDefaultValue = true, asyncAfterDefaultValue = false) + + val generator = Camunda7ElementTemplateGenerator(c7Config = config, inputValueNamingPolicy = InputValueNamingPolicy.ATTRIBUTE_NAME) + + val info = ProcessEngineWorkerDocumentationInfo( + name = "My Worker", + description = "desc", + version = -1, + type = "myTopic", + inputProperties = listOf( + ProcessEngineWorkerPropertyInfo( + name = "foo", + label = "Foo", + type = PropertyType.STRING, + editable = true, + notEmpty = false + ) + ), + outputProperties = listOf( + ProcessEngineWorkerPropertyInfo( + name = "bar", + label = "Bar", + type = PropertyType.STRING, + editable = true, + notEmpty = false + ) + ) + ) + + val result = generator.generate(info) + + assertThat(result) + .hasFieldOrPropertyWithValue("name", info.name) + .hasFieldOrPropertyWithValue("fileName", "My-Worker.json") + .hasFieldOrPropertyWithValue("engine", TargetPlattform.C7) + assertThat(result.content) + .contains("\"\$schema\"") + .contains("\"name\" : \"My Worker\"") + .contains("\"id\" : \"myTopic\"") + .contains("\"camunda:type\"") + .contains("\"external\"") + .contains("\"camunda:topic\"") + .contains("\"myTopic\"") + .contains("Input: Foo") + .contains("Output: Bar") + .contains("Async Before") + .contains("Async After") + } + +} diff --git a/documentation/documentation-c8-maven-plugin/pom.xml b/documentation/documentation-c8-maven-plugin/pom.xml new file mode 100644 index 0000000..eda439c --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation + 0.6.1-SNAPSHOT + + + process-engine-worker-documentation-c8-maven-plugin + ${project.artifactId} + maven-plugin + Maven plugin to generate camunda 8 element templates + + + 3.9.11 + 3.15.1 + 0.10.2 + 3.15.1 + + + + + + org.apache.maven + maven-plugin-api + ${org.apache.maven.version} + + + + org.apache.maven + maven-core + ${org.apache.maven.version} + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${org.apache.maven.plugin-tools.version} + provided + + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${org.apache.maven-plugins.version} + + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${org.apache.maven-plugins.version} + + + + + + diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojo.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojo.kt new file mode 100644 index 0000000..de39dca --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojo.kt @@ -0,0 +1,65 @@ +package dev.bpmcrafters.processengine.worker.documentation + +import dev.bpmcrafters.processengine.worker.documentation.core.WorkerDocumentationGenerator +import dev.bpmcrafters.processengine.worker.documentation.engine.Camunda8ElementTemplateGenerator +import dev.bpmcrafters.processengine.worker.documentation.generator.api.DocumentationFailedException +import dev.bpmcrafters.processengine.worker.documentation.generator.api.EngineDocumentationGeneratorApi +import org.apache.maven.plugin.AbstractMojo +import org.apache.maven.plugin.MojoExecutionException +import org.apache.maven.plugin.logging.Log +import org.apache.maven.plugins.annotations.LifecyclePhase +import org.apache.maven.plugins.annotations.Mojo +import org.apache.maven.plugins.annotations.Parameter +import org.apache.maven.plugins.annotations.ResolutionScope +import org.apache.maven.project.MavenProject +import java.io.File + +@Mojo(name = "generate", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.RUNTIME) +class WorkerDocumentationGeneratorMojo: AbstractMojo() { + + private val log: Log = getLog() + + @Parameter(defaultValue = "\${project}", required = true, readonly = true) + var project: MavenProject? = null + + /** + * Directory to output the generated element templates to. + */ + @Parameter(property = "elementtemplategen.outputDirectory", defaultValue = "\${project.build.directory/generated-sources/element-templates}") + var outputDirectory: File? = null + + /** + * A flag indicating if the output directory should be cleaned before generation. + */ + @Parameter(name = "clean", property = "elementtemplategen.clean", defaultValue = "false") + var clean: Boolean = false + + // Minimal factory hook for testing/injection + var generatorFactory: (MavenProject, File, EngineDocumentationGeneratorApi) -> WorkerDocumentationGenerator = + { project, outputDir, generator -> WorkerDocumentationGenerator(project, outputDir, generator) } + + override fun execute() { + log.info("Generating element templates for ${project?.artifactId}") + + val workerDocumentationGenerator = generatorFactory( + requireNotNull(project), + requireNotNull(outputDirectory), + Camunda8ElementTemplateGenerator() + ) + + try { + if (clean) { + log.info("Cleaning output directory...") + workerDocumentationGenerator.clean() + } + log.info("Generate worker documentation") + workerDocumentationGenerator.generate() + } catch (e: DocumentationFailedException) { + log.error(e.message) + log.debug(e) + throw MojoExecutionException(e.message) + } + + } + +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda8ElementTemplateGenerator.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda8ElementTemplateGenerator.kt new file mode 100644 index 0000000..bf85eeb --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda8ElementTemplateGenerator.kt @@ -0,0 +1,105 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine + +import dev.bpmcrafters.processengine.worker.documentation.api.PropertyType +import dev.bpmcrafters.processengine.worker.documentation.core.SchemaJsonConverter +import dev.bpmcrafters.processengine.worker.documentation.engine.schema.Binding +import dev.bpmcrafters.processengine.worker.documentation.engine.schema.CamundaC8ElementTemplate +import dev.bpmcrafters.processengine.worker.documentation.engine.schema.Constraints +import dev.bpmcrafters.processengine.worker.documentation.engine.schema.Property +import dev.bpmcrafters.processengine.worker.documentation.generator.api.* + +class Camunda8ElementTemplateGenerator( + private val inputValueNamingPolicy: InputValueNamingPolicy = InputValueNamingPolicy.EMPTY, +): EngineDocumentationGeneratorApi { + + override fun generate( + processEngineWorkerDocumentationInfo: ProcessEngineWorkerDocumentationInfo, + ): GenerationResult { + + val elementTemplate = CamundaC8ElementTemplate() + elementTemplate.name = processEngineWorkerDocumentationInfo.name + elementTemplate.id = processEngineWorkerDocumentationInfo.type + elementTemplate.appliesTo = mutableListOf(BPMNElementType.BPMN_SERVICE_TASK.value) + + + if (processEngineWorkerDocumentationInfo.version > 0) { + elementTemplate.version = processEngineWorkerDocumentationInfo.version + } + + val implementationTopicProperty = Property() + implementationTopicProperty.label = "Topic" + implementationTopicProperty.type = PropertyType.STRING.type + implementationTopicProperty.editable = false + val binding = Binding() + binding.type = Binding.Type.ZEEBE_TASKDEFINITION_TYPE + implementationTopicProperty.binding = binding + + elementTemplate.properties.add(implementationTopicProperty) + + for (inputProperty in processEngineWorkerDocumentationInfo.inputProperties) { + val property = createInputParameterProp(inputProperty, inputValueNamingPolicy) + elementTemplate.properties.add(property) + } + + for (ouputProperty in processEngineWorkerDocumentationInfo.outputProperties) { + val property = createOutputParameterProp(ouputProperty, inputValueNamingPolicy) + elementTemplate.properties.add(property) + } + + val jsonSchema = "https://unpkg.com/@camunda/element-templates-json-schema@0.1.0/resources/schema.json" + val json = SchemaJsonConverter.toJsonString(jsonSchema, elementTemplate, CamundaC8ElementTemplate::class.java) + + return GenerationResult( + name = processEngineWorkerDocumentationInfo.name, + version = processEngineWorkerDocumentationInfo.version, + content = json, + fileName = processEngineWorkerDocumentationInfo.name.replace(" ", "-") + ".json", + engine = TargetPlattform.C8 + ) + } + + private fun createInputParameterProp( + info: ProcessEngineWorkerPropertyInfo, + inputValueNamingPolicy: InputValueNamingPolicy + ): Property { + val property = Property() + property.label = "Input: " + info.label + property.value = if (InputValueNamingPolicy.ATTRIBUTE_NAME == inputValueNamingPolicy) "=" + info.name else "=" + property.type = info.type.type + val binding = Binding() + binding.type = Binding.Type.ZEEBE_INPUT + binding.name = info.name + property.binding = binding + + val constraint = Constraints() + constraint.notEmpty = info.notEmpty + property.constraints = constraint + + property.editable = info.editable + + return property + } + + private fun createOutputParameterProp( + info: ProcessEngineWorkerPropertyInfo, + inputValueNamingPolicy: InputValueNamingPolicy + ): Property { + val property = Property() + property.label = "Output: " + info.label + property.value = info.name + property.type = info.type.type + val binding = Binding() + binding.type = Binding.Type.ZEEBE_OUTPUT + binding.source = "=" + info.name + property.binding = binding + + val constraint = Constraints() + constraint.notEmpty = info.notEmpty + property.constraints = constraint + + property.editable = info.editable + + return property + } + +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Binding.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Binding.kt new file mode 100644 index 0000000..988c818 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Binding.kt @@ -0,0 +1,71 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder +import com.fasterxml.jackson.annotation.JsonValue + +/** + * property binding + * Specifying how the property is mapped to BPMN or Zeebe extension elements and attributes + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "type", + "name", + "source", + "key" + ] +) +class Binding { + /** + * The type of a property binding (Required) + */ + @JsonProperty("type") + @JsonPropertyDescription("The type of a property binding") + var type: Type? = null + + /** + * The name of a property binding + */ + @JsonProperty("name") + @JsonPropertyDescription("The name of a property binding") + var name: String? = null + + /** + * The source value of a property binding (zeebe:output) + */ + @JsonProperty("source") + @JsonPropertyDescription("The source value of a property binding (zeebe:output)") + var source: String? = null + + /** + * The key value of a property binding (zeebe:taskHeader) + */ + @JsonProperty("key") + @JsonPropertyDescription("The key value of a property binding (zeebe:taskHeader)") + var key: String? = null + + /** + * property binding type + */ + enum class Type(@get:JsonValue val value: String) { + PROPERTY("property"), + ZEEBE_TASKDEFINITION_TYPE("zeebe:taskDefinition:type"), + ZEEBE_TASKDEFINITION_RETRIES("zeebe:taskDefinition:retries"), + ZEEBE_INPUT("zeebe:input"), + ZEEBE_OUTPUT("zeebe:output"), + ZEEBE_PROPERTY("zeebe:property"), + ZEEBE_TASKHEADER("zeebe:taskHeader"); + + companion object { + @JvmStatic + @JsonCreator + fun fromValue(value: String): Type = values().find { it.value == value } + ?: throw IllegalArgumentException(value) + } + } +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/CamundaC8ElementTemplate.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/CamundaC8ElementTemplate.kt new file mode 100644 index 0000000..3dd532d --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/CamundaC8ElementTemplate.kt @@ -0,0 +1,96 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * Element Template Schema + * An element template configuration + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "name", + "id", + "description", + "version", + "isDefault", + "appliesTo", + "elementType", + "metadata", + "entriesVisible", + "groups", + "documentationRef", + "properties", + "icon" + ] +) +class CamundaC8ElementTemplate { + /** The name of the element template (Required) */ + @JsonProperty("name") + @JsonPropertyDescription("The name of the element template") + var name: String? = null + + /** The identifier of the element template (Required) */ + @JsonProperty("id") + @JsonPropertyDescription("The identifier of the element template") + var id: String? = null + + /** The description of the element template */ + @JsonProperty("description") + @JsonPropertyDescription("The description of the element template") + var description: String? = null + + /** Optional version of the template. Unique by id+version */ + @JsonProperty("version") + @JsonPropertyDescription( + "Optional version of the template. If you add a version to a template it will be considered unique based on its ID and version. Two templates can have the same ID if their version is different" + ) + var version: Int? = null + + /** Indicates whether the element template is a default template */ + @JsonProperty("isDefault") + @JsonPropertyDescription("Indicates whether the element template is a default template") + var isDefault: Boolean? = null + + /** List of BPMN types the template can be applied to (Required) */ + @JsonProperty("appliesTo") + @JsonPropertyDescription("List of BPMN types the template can be applied to") + var appliesTo: MutableList = ArrayList() + + /** The BPMN type the element will be transformed into */ + @JsonProperty("elementType") + @JsonPropertyDescription("The BPMN type the element will be transformed into") + var elementType: ElementType? = null + + /** Some custom properties for further configuration */ + @JsonProperty("metadata") + @JsonPropertyDescription("Some custom properties for further configuration") + var metadata: Metadata? = null + + /** Select whether non-template entries are visible in the properties panel */ + @JsonProperty("entriesVisible") + @JsonPropertyDescription("Select whether non-template entries are visible in the properties panel") + var entriesVisible: Boolean? = null + + /** Custom fields can be ordered together via groups */ + @JsonProperty("groups") + @JsonPropertyDescription("Custom fields can be ordered together via groups") + var groups: MutableList = ArrayList() + + /** element template documentationRef */ + @JsonProperty("documentationRef") + var documentationRef: String? = null + + /** List of properties of the element template (Required) */ + @JsonProperty("properties") + @JsonPropertyDescription("List of properties of the element template") + var properties: MutableList = ArrayList() + + /** Custom icon to be shown on the element */ + @JsonProperty("icon") + @JsonPropertyDescription("Custom icon to be shown on the element") + var icon: Icon? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Choice.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Choice.kt new file mode 100644 index 0000000..fb1f8fb --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Choice.kt @@ -0,0 +1,29 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * property choice + * The choices for dropdown fields + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "name", + "value" + ] +) +class Choice { + /** The name of a choice (Required) */ + @JsonProperty("name") + @JsonPropertyDescription("The name of a choice") + var name: String? = null + + /** The value of a choice (Required) */ + @JsonProperty("value") + @JsonPropertyDescription("The value of a choice") + var value: String? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Condition.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Condition.kt new file mode 100644 index 0000000..0dc9181 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Condition.kt @@ -0,0 +1,3 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +interface Condition diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Constraints.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Constraints.kt new file mode 100644 index 0000000..3b28f21 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Constraints.kt @@ -0,0 +1,41 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * property constraints + * The validation constraints of a control field + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "notEmpty", + "minLength", + "maxLength", + "pattern" + ] +) +class Constraints { + /** The control field must not be empty */ + @JsonProperty("notEmpty") + @JsonPropertyDescription("The control field must not be empty") + var notEmpty: Boolean? = null + + /** The minimal length of a control field value */ + @JsonProperty("minLength") + @JsonPropertyDescription("The minimal length of a control field value") + var minLength: Double? = null + + /** The maximal length of a control field value */ + @JsonProperty("maxLength") + @JsonPropertyDescription("The maximal length of a control field value") + var maxLength: Double? = null + + /** A regular expression pattern for a constraint */ + @JsonProperty("pattern") + @JsonPropertyDescription("A regular expression pattern for a constraint") + var pattern: String? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/ElementType.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/ElementType.kt new file mode 100644 index 0000000..d0a9454 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/ElementType.kt @@ -0,0 +1,24 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * element type + * The BPMN type the element will be transformed into + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "value" + ] +) +class ElementType { + /** + * element type value + * The identifier of the element template + */ + @JsonProperty("value") + var value: String? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Equals.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Equals.kt new file mode 100644 index 0000000..210d01d --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Equals.kt @@ -0,0 +1,20 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * condition equals + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "equals" + ] +) +class Equals : Expression { + /** condition equals */ + @JsonProperty("equals") + var equals: Any? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Expression.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Expression.kt new file mode 100644 index 0000000..203d165 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Expression.kt @@ -0,0 +1,3 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +interface Expression diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Group.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Group.kt new file mode 100644 index 0000000..4983390 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Group.kt @@ -0,0 +1,29 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * element template groups + * Custom fields can be ordered together via groups + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "id", + "label" + ] +) +class Group { + /** The id of the custom group (Required) */ + @JsonProperty("id") + @JsonPropertyDescription("The id of the custom group") + var id: String? = null + + /** The label of the custom group (Required) */ + @JsonProperty("label") + @JsonPropertyDescription("The label of the custom group") + var label: String? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Icon.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Icon.kt new file mode 100644 index 0000000..3efa185 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Icon.kt @@ -0,0 +1,23 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * element template icon + * Custom icon to be shown on the element + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "contents" + ] +) +class Icon { + /** The URL of an icon (Required) */ + @JsonProperty("contents") + @JsonPropertyDescription("The URL of an icon") + var contents: String? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Metadata.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Metadata.kt new file mode 100644 index 0000000..cca1120 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Metadata.kt @@ -0,0 +1,17 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonInclude +import java.util.LinkedHashMap + +/** + * element template metadata + * + * Some custom properties for further configuration + */ +@JsonInclude(JsonInclude.Include.NON_NULL) + +data class Metadata( + @field:JsonIgnore + var additionalProperties: MutableMap = LinkedHashMap() +) diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/MultipleConditions.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/MultipleConditions.kt new file mode 100644 index 0000000..07710a3 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/MultipleConditions.kt @@ -0,0 +1,21 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * property condition + * Condition(s) to activate the binding + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "allMatch" + ] +) +class MultipleConditions : Condition { + /** condition allMatch (Required) */ + @JsonProperty("allMatch") + var allMatch: MutableList = ArrayList() +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/OneOf.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/OneOf.kt new file mode 100644 index 0000000..ddd9dea --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/OneOf.kt @@ -0,0 +1,20 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * condition oneOf + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "oneOf" + ] +) +class OneOf : Expression { + /** condition oneOf */ + @JsonProperty("oneOf") + var oneOf: MutableList = ArrayList() +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Property.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Property.kt new file mode 100644 index 0000000..d82627a --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/Property.kt @@ -0,0 +1,101 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder + +/** + * element template property + * List of properties of the element template + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "id", + "value", + "description", + "label", + "type", + "editable", + "choices", + "constraints", + "group", + "condition", + "binding", + "optional", + "feel", + "language" + ] +) +class Property { + /** Unique identifier of the property */ + @JsonProperty("id") + @JsonPropertyDescription("Unique identifier of the property") + var id: String? = null + + /** The value of a control field */ + @JsonProperty("value") + @JsonPropertyDescription("The value of a control field") + var value: Any? = null + + /** The description of a control field */ + @JsonProperty("description") + @JsonPropertyDescription("The description of a control field") + var description: String? = null + + /** The label of a control field */ + @JsonProperty("label") + @JsonPropertyDescription("The label of a control field") + var label: String? = null + + /** The type of a control field */ + @JsonProperty("type") + @JsonPropertyDescription("The type of a control field") + var type: String? = null + + /** Indicates whether a control field is editable or not */ + @JsonProperty("editable") + @JsonPropertyDescription("Indicates whether a control field is editable or not") + var editable: Boolean? = null + + /** The choices for dropdown fields */ + @JsonProperty("choices") + @JsonPropertyDescription("The choices for dropdown fields") + var choices: MutableList = ArrayList() + + /** The validation constraints of a control field */ + @JsonProperty("constraints") + @JsonPropertyDescription("The validation constraints of a control field") + var constraints: Constraints? = null + + /** The custom group of a control field */ + @JsonProperty("group") + @JsonPropertyDescription("The custom group of a control field") + var group: String? = null + + /** Condition(s) to activate the binding */ + @JsonProperty("condition") + @JsonPropertyDescription("Condition(s) to activate the binding") + var condition: Condition? = null + + /** Specifying how the property is mapped to BPMN or Zeebe extension elements and attributes (Required) */ + @JsonProperty("binding") + @JsonPropertyDescription("Specifying how the property is mapped to BPMN or Zeebe extension elements and attributes") + var binding: Binding? = null + + /** Indicates whether a property is optional. Optional bindings do not persist empty values in the underlying BPMN 2.0 XML */ + @JsonProperty("optional") + @JsonPropertyDescription("Indicates whether a property is optional. Optional bindings do not persist empty values in the underlying BPMN 2.0 XML") + var optional: Boolean? = null + + /** Indicates whether the property can be a feel expression */ + @JsonProperty("feel") + @JsonPropertyDescription("Indicates whether the property can be a feel expression") + var feel: String? = null + + /** Indicates that the field is a custom language editor */ + @JsonProperty("language") + @JsonPropertyDescription("Indicates that the field is a custom language editor") + var language: String? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/SingleCondition.kt b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/SingleCondition.kt new file mode 100644 index 0000000..5626046 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/schema/SingleCondition.kt @@ -0,0 +1,35 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine.schema + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonPropertyDescription +import com.fasterxml.jackson.annotation.JsonPropertyOrder +import com.fasterxml.jackson.annotation.JsonUnwrapped + +/** + * property condition + * Condition(s) to activate the binding + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder( + value = [ + "type", + "property", + "expression", + ] +) +class SingleCondition : Condition { + /** The type of the condition */ + @JsonProperty("type") + @JsonPropertyDescription("The type of the condition") + val type: String = "simple" + + /** The id of the property to check (Required) */ + @JsonProperty("property") + @JsonPropertyDescription("The id of the property to check") + var property: String? = null + + /** condition expression */ + @JsonUnwrapped + var expression: Expression? = null +} diff --git a/documentation/documentation-c8-maven-plugin/src/main/resources/schema/camunda-c8-element-template.json b/documentation/documentation-c8-maven-plugin/src/main/resources/schema/camunda-c8-element-template.json new file mode 100644 index 0000000..ab0c730 --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/main/resources/schema/camunda-c8-element-template.json @@ -0,0 +1,812 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://camunda.org/schema/zeebe-element-templates/1.0", + "title": "Element Template Schema", + "definitions": { + "properties": { + "allOf": [ + { + "type": "array", + "description": "List of properties of the element template.", + "allOf": [ + { + "examples": [ + [ + { + "label": "Name", + "type": "String", + "binding": { + "type": "property", + "name": "name" + } + } + ] + ] + } + ], + "items": { + "type": "object", + "default": {}, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "Dropdown" + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "choices" + ] + } + } + ], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier of the property." + }, + "value": { + "$id": "#/properties/property/value", + "type": [ + "string", + "boolean" + ], + "description": "The value of a control field." + }, + "description": { + "$id": "#/properties/property/description", + "type": "string", + "description": "The description of a control field." + }, + "label": { + "$id": "#/properties/property/label", + "type": "string", + "description": "The label of a control field." + }, + "type": { + "$id": "#/properties/property/type", + "type": "string", + "description": "The type of a control field." + }, + "editable": { + "$id": "#/properties/property/editable", + "type": "boolean", + "description": "Indicates whether a control field is editable or not." + }, + "choices": { + "$id": "#/properties/property/choices", + "type": "array", + "description": "The choices for dropdown fields.", + "default": [], + "items": { + "$id": "#/properties/property/choices/item", + "type": "object", + "default": {}, + "properties": { + "name": { + "$id": "#/properties/property/choices/item/name", + "type": "string", + "description": "The name of a choice." + }, + "value": { + "$id": "#/properties/property/choices/item/value", + "type": "string", + "description": "The value of a choice." + } + }, + "required": [ + "value", + "name" + ] + } + }, + "constraints": { + "$id": "#/properties/property/constraints", + "type": "object", + "description": "The validation constraints of a control field.", + "allOf": [ + { + "examples": [ + { + "notEmpty": true + } + ] + } + ], + "properties": { + "notEmpty": { + "$id": "#/properties/property/constraints/notEmpty", + "type": "boolean", + "description": "The control field must not be empty." + }, + "minLength": { + "$id": "#/properties/property/constraints/minLength", + "type": "number", + "description": "The minimal length of a control field value." + }, + "maxLength": { + "$id": "#/properties/property/constraints/maxLength", + "type": "number", + "description": "The maximal length for a control field value." + }, + "pattern": { + "$id": "#/properties/property/constraints/pattern", + "description": "A regular expression pattern for a constraint.", + "oneOf": [ + { + "type": "object", + "default": {}, + "properties": { + "value": { + "$id": "#/properties/property/constraints/pattern/value", + "type": "string", + "description": "The regular expression of a pattern." + }, + "message": { + "$id": "#/properties/property/constraints/pattern/message", + "type": "string", + "description": "The validation message of a pattern." + } + } + }, + { + "type": "string" + } + ] + } + } + }, + "group": { + "$id": "#/properties/property/group", + "type": "string", + "description": "The custom group of a control field." + }, + "condition": { + "$id": "#/condition", + "type": "object", + "description": "Condition(s) to activate the binding.", + "allOf": [ + { + "examples": [ + { + "type": "simple", + "property": "httpMethod", + "equals": "GET" + }, + { + "type": "simple", + "property": "httpMethod", + "oneOf": [ + "POST", + "PUT", + "DELETE" + ] + }, + { + "allMatch": [ + { + "type": "simple", + "property": "authType", + "equals": "Basic" + }, + { + "type": "simple", + "property": "httpMethod", + "oneOf": [ + "POST", + "PUT", + "DELETE" + ] + } + ] + } + ] + } + ], + "definitions": { + "condition": { + "type": "object", + "required": [ + "property" + ], + "properties": { + "type": { + "$id": "#/condition/type", + "const": "simple", + "description": "The type of the condition.", + "default": "simple" + }, + "property": { + "$id": "#/condition/property", + "type": "string", + "description": "The id of the property to check." + } + }, + "oneOf": [ + { + "properties": { + "equals": { + "type": [ + "string", + "number", + "boolean" + ] + } + }, + "required": [ + "equals" + ] + }, + { + "properties": { + "oneOf": { + "type": "array", + "items": { + "type": [ + "string", + "number" + ] + } + } + }, + "required": [ + "oneOf" + ] + } + ] + } + }, + "oneOf": [ + { + "$ref": "#/definitions/properties/allOf/0/items/properties/condition/definitions/condition" + }, + { + "properties": { + "allMatch": { + "$id": "#/allMatch", + "type": "array", + "items": { + "$ref": "#/definitions/properties/allOf/0/items/properties/condition/definitions/condition" + }, + "minItems": 1 + } + }, + "required": [ + "allMatch" + ] + } + ] + } + } + } + }, + { + "$schema": "http://json-schema.org/draft-07/schema", + "type": "array", + "description": "List of properties of the element template.", + "items": { + "type": "object", + "default": {}, + "required": [ + "binding" + ], + "allOf": [ + { + "if": { + "properties": { + "binding": { + "properties": { + "type": { + "const": "property" + } + }, + "required": [ + "type" + ] + } + }, + "required": [ + "binding" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "String", + "Text", + "Hidden", + "Dropdown", + "Boolean" + ] + } + } + } + }, + { + "if": { + "properties": { + "binding": { + "properties": { + "type": { + "enum": [ + "zeebe:input", + "zeebe:output", + "zeebe:property", + "zeebe:taskHeader", + "zeebe:taskDefinition:retries", + "zeebe:taskDefinition:type" + ] + } + }, + "required": [ + "type" + ] + } + }, + "required": [ + "binding" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "String", + "Text", + "Hidden", + "Dropdown" + ] + } + } + } + }, + { + "if": { + "properties": { + "optional": { + "const": true + } + }, + "required": [ + "optional" + ] + }, + "then": { + "properties": { + "binding": { + "properties": { + "type": { + "enum": [ + "zeebe:input", + "zeebe:output", + "zeebe:property", + "zeebe:taskHeader" + ] + } + }, + "required": [ + "type" + ] + } + } + } + }, + { + "if": { + "properties": { + "optional": { + "const": true + } + }, + "required": [ + "optional" + ] + }, + "then": { + "properties": { + "constraints": { + "properties": { + "notEmpty": { + "const": false + } + }, + "required": [ + "notEmpty" + ] + } + } + } + }, + { + "if": { + "properties": { + "feel": { + "not": { + "const": null + } + } + }, + "required": [ + "feel" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "String", + "Text" + ] + } + }, + "required": [ + "type" + ] + } + }, + { + "if": { + "properties": { + "language": { + "not": { + "const": null + } + } + }, + "required": [ + "language" + ] + }, + "then": { + "properties": { + "type": { + "enum": [ + "Text" + ] + } + } + } + } + ], + "properties": { + "binding": { + "$id": "#/properties/property/binding", + "type": "object", + "description": "Specifying how the property is mapped to BPMN or Zeebe extension elements and attributes.", + "required": [ + "type" + ], + "allOf": [ + { + "if": { + "properties": { + "type": { + "enum": [ + "property", + "zeebe:property", + "zeebe:input" + ] + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "name" + ] + } + }, + { + "if": { + "properties": { + "type": { + "const": "zeebe:output" + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "source" + ] + } + }, + { + "if": { + "properties": { + "type": { + "const": "zeebe:taskHeader" + } + }, + "required": [ + "type" + ] + }, + "then": { + "required": [ + "key" + ] + } + }, + { + "examples": [ + { + "type": "property", + "name": "name" + }, + { + "type": "zeebe:input", + "name": "input" + }, + { + "type": "zeebe:output", + "source": "output" + }, + { + "type": "zeebe:property", + "name": "property" + }, + { + "type": "zeebe:taskDefinition:retries" + }, + { + "type": "zeebe:taskDefinition:type" + }, + { + "type": "zeebe:taskHeader", + "key": "key" + } + ] + } + ], + "properties": { + "type": { + "$id": "#/properties/property/binding/type", + "type": "string", + "description": "The type of a property binding.", + "enum": [ + "property", + "zeebe:taskDefinition:type", + "zeebe:taskDefinition:retries", + "zeebe:input", + "zeebe:output", + "zeebe:property", + "zeebe:taskHeader" + ] + }, + "name": { + "$id": "#/properties/property/binding/name", + "type": "string", + "description": "The name of a property binding." + }, + "source": { + "$id": "#/properties/property/binding/source", + "type": "string", + "description": "The source value of a property binding (zeebe:output)." + }, + "key": { + "$id": "#/properties/property/binding/key", + "type": "string", + "description": "The key value of a property binding (zeebe:taskHeader)." + } + } + }, + "optional": { + "$id": "#/optional", + "type": "boolean", + "description": "Indicates whether a property is optional. Optional bindings do not persist empty values in the underlying BPMN 2.0 XML." + }, + "feel": { + "$id": "#/properties/property/feel", + "type": "string", + "default": null, + "description": "Indicates whether the property can be a feel expression", + "enum": [ + null, + "optional", + "required" + ] + }, + "language": { + "$id": "#/properties/property/language", + "type": "string", + "description": "Indicates that the field is a custom language editor" + } + } + } + } + ] + }, + "template": { + "type": "object", + "allOf": [ + { + "required": [ + "name", + "id", + "appliesTo", + "properties" + ], + "properties": { + "name": { + "$id": "#/name", + "type": "string", + "description": "The name of the element template." + }, + "id": { + "$id": "#/id", + "type": "string", + "description": "The identifier of the element template." + }, + "description": { + "$id": "#/description", + "type": "string", + "description": "The description of the element template." + }, + "version": { + "$id": "#/version", + "type": "integer", + "description": "Optional version of the template. If you add a version to a template it will be considered unique based on its ID and version. Two templates can have the same ID if their version is different." + }, + "isDefault": { + "$id": "#/isDefault", + "type": "boolean", + "description": "Indicates whether the element template is a default template." + }, + "appliesTo": { + "$id": "#/appliesTo", + "type": "array", + "description": "List of BPMN types the template can be applied to.", + "default": [], + "items": { + "$id": "#/appliesTo/items", + "type": "string", + "pattern": "^[\\w\\d]+:[\\w\\d]+$", + "allOf": [ + { + "examples": [ + "bpmn:Task", + "bpmn:ServiceTask", + "bpmn:SequenceFlow", + "bpmn:Process", + "bpmn:StartEvent", + "bpmn:Gateway" + ] + } + ] + } + }, + "elementType": { + "$id": "#/elementType", + "type": "object", + "description": "The BPMN type the element will be transformed into.", + "default": {}, + "required": [ + "value" + ], + "properties": { + "value": { + "$id": "#/elementType/value", + "type": "string", + "pattern": "^[\\w\\d]+:[\\w\\d]+$", + "allOf": [ + { + "examples": [ + "bpmn:ServiceTask", + "bpmn:UserTask", + "bpmn:StartEvent", + "bpmn:ExclusiveGateway", + "bpmn:ParallelGateway" + ] + } + ] + } + } + }, + "metadata": { + "$id": "#/metadata", + "type": "object", + "description": "Some custom properties for further configuration.", + "default": {} + }, + "entriesVisible": { + "$id": "#/entriesVisible", + "type": "boolean", + "description": "Select whether non-template entries are visible in the properties panel." + }, + "groups": { + "$id": "#/groups", + "type": "array", + "description": "Custom fields can be ordered together via groups.", + "allOf": [ + { + "examples": [ + [ + { + "id": "group-1", + "label": "My Group" + } + ] + ] + } + ], + "items": { + "$id": "#/groups/group", + "type": "object", + "default": {}, + "required": [ + "id", + "label" + ], + "properties": { + "id": { + "$id": "#/groups/group/id", + "type": "string", + "description": "The id of the custom group" + }, + "label": { + "$id": "#/groups/group/label", + "type": "string", + "description": "The label of the custom group" + } + } + } + }, + "documentationRef": { + "$id": "#/documentationRef", + "type": "string", + "pattern": "^(https|http)://.*" + } + } + } + ], + "properties": { + "properties": { + "$ref": "#/definitions/properties", + "$id": "#/properties" + }, + "icon": { + "$id": "#/icon", + "type": "object", + "description": "Custom icon to be shown on the element", + "default": {}, + "properties": { + "contents": { + "$id": "#/icon/contents", + "type": "string", + "description": "The URL of an icon.", + "pattern": "^(https?|data):.*" + } + }, + "required": [ + "contents" + ] + } + } + } + }, + "oneOf": [ + { + "description": "An element template configuration.", + "$ref": "#/definitions/template" + }, + { + "type": "array", + "description": "A list of element template configurations.", + "items": { + "$ref": "#/definitions/template" + } + } + ] +} diff --git a/documentation/documentation-c8-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojoTest.kt b/documentation/documentation-c8-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojoTest.kt new file mode 100644 index 0000000..da1607a --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/WorkerDocumentationGeneratorMojoTest.kt @@ -0,0 +1,67 @@ +package dev.bpmcrafters.processengine.worker.documentation + +import dev.bpmcrafters.processengine.worker.documentation.core.WorkerDocumentationGenerator +import dev.bpmcrafters.processengine.worker.documentation.generator.api.DocumentationFailedException +import org.apache.maven.plugin.MojoExecutionException +import org.apache.maven.project.MavenProject +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import java.io.File + +class WorkerDocumentationGeneratorMojoTest { + + @Test + fun `execute with clean true calls clean and generate`() { + val mojo = WorkerDocumentationGeneratorMojo().apply { + project = mock() + outputDirectory = mock() + clean = true + } + + val mockGenerator = mock() + mojo.generatorFactory = { _, _, _ -> mockGenerator } + + mojo.execute() + + verify(mockGenerator).clean() + verify(mockGenerator).generate() + } + + @Test + fun `execute with clean false calls only generate`() { + val mojo = WorkerDocumentationGeneratorMojo().apply { + project = mock() + outputDirectory = mock() + clean = false + } + + val mockGenerator = mock() + mojo.generatorFactory = { _, _, _ -> mockGenerator } + + mojo.execute() + + verify(mockGenerator, never()).clean() + verify(mockGenerator).generate() + } + + @Test + fun `execute wraps DocumentationFailedException into MojoExecutionException`() { + val mojo = WorkerDocumentationGeneratorMojo().apply { + project = mock() + outputDirectory = mock() + clean = false + } + + val failingGenerator = mock { + on { generate() } doThrow DocumentationFailedException("boom", null) + } + mojo.generatorFactory = { _, _, _ -> failingGenerator } + + assertThatThrownBy { mojo.execute() } + .isInstanceOf(MojoExecutionException::class.java) + } +} diff --git a/documentation/documentation-c8-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda8ElementTemplateGeneratorTest.kt b/documentation/documentation-c8-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda8ElementTemplateGeneratorTest.kt new file mode 100644 index 0000000..3b8439f --- /dev/null +++ b/documentation/documentation-c8-maven-plugin/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/engine/Camunda8ElementTemplateGeneratorTest.kt @@ -0,0 +1,59 @@ +package dev.bpmcrafters.processengine.worker.documentation.engine + +import dev.bpmcrafters.processengine.worker.documentation.api.PropertyType +import dev.bpmcrafters.processengine.worker.documentation.generator.api.InputValueNamingPolicy +import dev.bpmcrafters.processengine.worker.documentation.generator.api.ProcessEngineWorkerDocumentationInfo +import dev.bpmcrafters.processengine.worker.documentation.generator.api.ProcessEngineWorkerPropertyInfo +import dev.bpmcrafters.processengine.worker.documentation.generator.api.TargetPlattform +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class Camunda8ElementTemplateGeneratorTest { + + @Test + fun `generate builds expected JSON and metadata`() { + val generator = Camunda8ElementTemplateGenerator(inputValueNamingPolicy = InputValueNamingPolicy.ATTRIBUTE_NAME) + + val info = ProcessEngineWorkerDocumentationInfo( + name = "My Worker", + description = "desc", + version = -1, + type = "myTopic", + inputProperties = listOf( + ProcessEngineWorkerPropertyInfo( + name = "foo", + label = "Foo", + type = PropertyType.STRING, + editable = true, + notEmpty = false + ) + ), + outputProperties = listOf( + ProcessEngineWorkerPropertyInfo( + name = "bar", + label = "Bar", + type = PropertyType.STRING, + editable = true, + notEmpty = false + ) + ) + ) + + val result = generator.generate(info) + + assertThat(result) + .hasFieldOrPropertyWithValue("name", info.name) + .hasFieldOrPropertyWithValue("fileName", "My-Worker.json") + .hasFieldOrPropertyWithValue("engine", TargetPlattform.C8) + assertThat(result.content) + .contains("\"\$schema\"") + .contains("\"name\" : \"My Worker\"") + .contains("\"id\" : \"myTopic\"") + .contains("\"zeebe:taskDefinition:type\"") + .contains("\"Topic\"") + .contains("\"myTopic\"") + .contains("Input: Foo") + .contains("Output: Bar") + } + +} diff --git a/documentation/documentation-core/pom.xml b/documentation/documentation-core/pom.xml new file mode 100644 index 0000000..9e36c6c --- /dev/null +++ b/documentation/documentation-core/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation + 0.6.1-SNAPSHOT + + + process-engine-worker-documentation-core + ${project.artifactId} + + + 3.9.11 + 3.15.1 + 0.10.2 + 3.15.1 + + + + + org.reflections + reflections + ${org.reflections.version} + + + + + org.apache.maven + maven-plugin-api + ${org.apache.maven.version} + + + + org.apache.maven + maven-core + ${org.apache.maven.version} + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${org.apache.maven.plugin-tools.version} + provided + + + + commons-io + commons-io + 2.20.0 + + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-spring-boot-starter + ${project.version} + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-api + ${project.version} + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-generator-api + ${project.version} + + + + diff --git a/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/SchemaJsonConverter.kt b/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/SchemaJsonConverter.kt new file mode 100644 index 0000000..b6229dc --- /dev/null +++ b/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/SchemaJsonConverter.kt @@ -0,0 +1,26 @@ +package dev.bpmcrafters.processengine.worker.documentation.core + +import com.fasterxml.jackson.core.JsonProcessingException +import com.fasterxml.jackson.databind.MapperFeature +import com.fasterxml.jackson.databind.json.JsonMapper + +class SchemaJsonConverter { + + companion object { + fun toJsonString(jsonSchema: String, content: T, contentClass: Class): String { + try { + val jsonSchema = "https://unpkg.com/@camunda/element-templates-json-schema@0.1.0/resources/schema.json" + val mapper = JsonMapper.builder() + .configure(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST, true) + .addMixIn(contentClass, SchemaMixin::class.java) + .build() + val objectWriter = mapper.writerFor(contentClass) + .withAttribute("\$schema", jsonSchema) + return objectWriter.withDefaultPrettyPrinter().writeValueAsString(content) + } catch (e: JsonProcessingException) { + throw RuntimeException("Could not generate json string!", e) + } + } + } + +} diff --git a/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/SchemaMixin.kt b/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/SchemaMixin.kt new file mode 100644 index 0000000..80cca3d --- /dev/null +++ b/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/SchemaMixin.kt @@ -0,0 +1,9 @@ +package dev.bpmcrafters.processengine.worker.documentation.core + +import com.fasterxml.jackson.databind.annotation.JsonAppend + +@JsonAppend(attrs = [JsonAppend.Attr(value = "\$schema")], prepend = true) +class SchemaMixin { + // Because the generated element template does not have a property to + // set the schema, we add it manually on serialization +} diff --git a/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/WorkerDocumentationGenerator.kt b/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/WorkerDocumentationGenerator.kt new file mode 100644 index 0000000..1434414 --- /dev/null +++ b/documentation/documentation-core/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/WorkerDocumentationGenerator.kt @@ -0,0 +1,101 @@ +package dev.bpmcrafters.processengine.worker.documentation.core + +import dev.bpmcrafters.processengine.worker.ProcessEngineWorker +import dev.bpmcrafters.processengine.worker.documentation.api.ProcessEngineWorkerDocumentation +import dev.bpmcrafters.processengine.worker.documentation.api.ProcessEngineWorkerPropertyDocumentation +import dev.bpmcrafters.processengine.worker.documentation.generator.api.DocumentationFailedException +import dev.bpmcrafters.processengine.worker.documentation.generator.api.EngineDocumentationGeneratorApi +import dev.bpmcrafters.processengine.worker.documentation.generator.api.ProcessEngineWorkerDocumentationInfo +import dev.bpmcrafters.processengine.worker.documentation.generator.api.ProcessEngineWorkerPropertyInfo +import org.apache.commons.io.FileUtils +import org.apache.maven.project.MavenProject +import org.reflections.Reflections +import org.reflections.scanners.Scanners +import org.reflections.util.ClasspathHelper +import org.reflections.util.ConfigurationBuilder +import java.io.File +import java.io.IOException +import java.net.URL +import java.net.URLClassLoader + +class WorkerDocumentationGenerator( + val project: MavenProject, + val outputDirectory: File, + val engineDocumentationGenerator: EngineDocumentationGeneratorApi +) { + + /** + * Clean the output directory + */ + fun clean() { + try { + FileUtils.deleteDirectory(outputDirectory) + } catch (e: IOException) { + throw DocumentationFailedException("Failed to clean output directory", e) + } + } + + /** + * Generates engine-specific documentation to the outputDirectory like e.g. c7 element templates + */ + fun generate() { + // Find annotated workers + val classpathURLs = project.compileClasspathElements + .map { File(it).toURI().toURL() } + val urlClassLoader = URLClassLoader(classpathURLs.toTypedArray(), javaClass.getClassLoader()) + val reflections = Reflections( + ConfigurationBuilder() + .setUrls(ClasspathHelper.forClassLoader(urlClassLoader)) + .addClassLoaders(urlClassLoader) + .addScanners(Scanners.MethodsAnnotated) + ) + val workers = reflections.getMethodsAnnotatedWith(ProcessEngineWorker::class.java) + + // generate documentation for each worker + workers.forEach { + if (it.parameterTypes.size > 1) { + throw DocumentationFailedException("Worker method ${it.name} has more than one parameter.", null) + } + + val inputParam = it.parameterTypes.first() + val returnType = it.returnType + + try { + val workerAnnotation = it.getAnnotation(ProcessEngineWorker::class.java) + val documentationAnnotation = it.getAnnotation(ProcessEngineWorkerDocumentation::class.java) + + val processEngineWorkerDocumentation = ProcessEngineWorkerDocumentationInfo( + documentationAnnotation.name, + documentationAnnotation.description, + documentationAnnotation.version, + workerAnnotation.topic, + generateProperties(inputParam), + generateProperties(returnType) + ) + + val result = engineDocumentationGenerator.generate(processEngineWorkerDocumentation) + + FileUtils.createParentDirectories(outputDirectory) + val workerDocumentation = File(outputDirectory, result.fileName) + FileUtils.write(workerDocumentation, result.content, "UTF-8") + } catch (e: NullPointerException) { + throw DocumentationFailedException("Worker method ${it.name} is missing @ProcessEngineWorkerDocumentation annotation.", e) + } + } + } + + private fun generateProperties(type: Class<*>): List { + return type.declaredFields.map { field -> + val annotation = field.getAnnotation(ProcessEngineWorkerPropertyDocumentation::class.java) + ProcessEngineWorkerPropertyInfo( + name = field.name, + label = annotation.label, + type = annotation.type, + editable = annotation.editable, + notEmpty = annotation.notEmpty, + ) + } + } + + +} diff --git a/documentation/documentation-core/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/WorkerDocumentationGeneratorTest.kt b/documentation/documentation-core/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/WorkerDocumentationGeneratorTest.kt new file mode 100644 index 0000000..91a2347 --- /dev/null +++ b/documentation/documentation-core/src/test/kotlin/dev/bpmcrafters/processengine/worker/documentation/core/WorkerDocumentationGeneratorTest.kt @@ -0,0 +1,99 @@ +package dev.bpmcrafters.processengine.worker.documentation.core + +import dev.bpmcrafters.processengine.worker.ProcessEngineWorker +import dev.bpmcrafters.processengine.worker.documentation.api.ProcessEngineWorkerDocumentation +import dev.bpmcrafters.processengine.worker.documentation.api.ProcessEngineWorkerPropertyDocumentation +import dev.bpmcrafters.processengine.worker.documentation.generator.api.EngineDocumentationGeneratorApi +import dev.bpmcrafters.processengine.worker.documentation.generator.api.GenerationResult +import dev.bpmcrafters.processengine.worker.documentation.generator.api.TargetPlattform +import org.apache.maven.project.MavenProject +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import org.mockito.Mockito.`when` +import org.mockito.kotlin.* +import java.io.File + +class WorkerDocumentationGeneratorTest { + + // Test helper types that will be discovered by Reflections + data class InDto( + @field:ProcessEngineWorkerPropertyDocumentation(label = "In Label") + val inField: String + ) + data class OutDto( + @field:ProcessEngineWorkerPropertyDocumentation(label = "Out Label") + val outField: String + ) + class TestWorkerClass { + @ProcessEngineWorker(topic = "testTopic") + @ProcessEngineWorkerDocumentation(name = "Test Worker", description = "desc", version = 1) + fun work(input: InDto): OutDto = OutDto("x") + } + + @TempDir + lateinit var tempDir: File + + private val generationResult = GenerationResult( + name = "Test Worker", + fileName = "Test-Worker.json", + content = "test", + version = 0, + engine = TargetPlattform.C7 + ) + + @Test + fun `generate creates element template file for annotated worker`() { + val testClassesPath = File(this::class.java.protectionDomain.codeSource.location.toURI()).path + + val project = mock { + on { compileClasspathElements } doReturn(listOf(testClassesPath)) + } + + val engineGenerator = mock() + `when`(engineGenerator.generate(any())).thenReturn(generationResult) + + val generator = WorkerDocumentationGenerator( + project = project, + outputDirectory = tempDir, + engineGenerator + ) + + generator.generate() + + val expectedFile = File(tempDir, "Test-Worker.json") + assertThat(expectedFile).exists() + val content = expectedFile.readText() + assertThat(content) + .isEqualTo("test") + verify(engineGenerator).generate(any()) + } + + @Test + fun `clean deletes output directory`() { + val project = mock { + on { compileClasspathElements } doReturn(emptyList()) + } + + val engineGenerator = mock() + `when`(engineGenerator.generate(any())).thenReturn(generationResult) + + // create a file inside + val testFile = File(tempDir, "some.txt").writeText("data") + assertThat(tempDir) + .exists() + assertThat(testFile) + + val generator = WorkerDocumentationGenerator( + project = project, + outputDirectory = tempDir, + engineGenerator + ) + + generator.clean() + + assertThat(tempDir.exists()) + .isFalse() + verify(engineGenerator, never()).generate(any()) + } +} diff --git a/documentation/generator-api/pom.xml b/documentation/generator-api/pom.xml new file mode 100644 index 0000000..fba139a --- /dev/null +++ b/documentation/generator-api/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation + 0.6.1-SNAPSHOT + + + process-engine-worker-documentation-generator-api + ${project.artifactId} + + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-api + ${project.version} + + + + diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/BPMNElementType.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/BPMNElementType.kt new file mode 100644 index 0000000..073a6eb --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/BPMNElementType.kt @@ -0,0 +1,7 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +enum class BPMNElementType(val value: String) { + BPMN_SERVICE_TASK("bpmn:ServiceTask"), + BPMN_SEND_TASK("bpmn:SendTask"), + BPMN_INTERMEDIATE_THROW_EVENT("bpmn:IntermediateThrowEvent") +} diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/DocumentationFailedException.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/DocumentationFailedException.kt new file mode 100644 index 0000000..deed8b8 --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/DocumentationFailedException.kt @@ -0,0 +1,4 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +class DocumentationFailedException(message: String, cause: Throwable?): RuntimeException(message, cause) { +} diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/EngineDocumentationGeneratorApi.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/EngineDocumentationGeneratorApi.kt new file mode 100644 index 0000000..6419381 --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/EngineDocumentationGeneratorApi.kt @@ -0,0 +1,7 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +interface EngineDocumentationGeneratorApi { + + fun generate(processEngineWorkerDocumentationInfo: ProcessEngineWorkerDocumentationInfo): GenerationResult + +} diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/GenerationResult.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/GenerationResult.kt new file mode 100644 index 0000000..980d56f --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/GenerationResult.kt @@ -0,0 +1,9 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +data class GenerationResult( + val name: String, + val version : Int, + val content: String, + val fileName: String, + val engine: TargetPlattform +) diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/InputValueNamingPolicy.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/InputValueNamingPolicy.kt new file mode 100644 index 0000000..6cdd07d --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/InputValueNamingPolicy.kt @@ -0,0 +1,6 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +enum class InputValueNamingPolicy { + EMPTY, + ATTRIBUTE_NAME +} diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/ProcessEngineWorkerDocumentationInfo.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/ProcessEngineWorkerDocumentationInfo.kt new file mode 100644 index 0000000..f4d27e0 --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/ProcessEngineWorkerDocumentationInfo.kt @@ -0,0 +1,10 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +data class ProcessEngineWorkerDocumentationInfo( + val name: String, + val description: String, + val version: Int, + val type: String, + val inputProperties: List, + val outputProperties: List +) diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/ProcessEngineWorkerPropertyInfo.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/ProcessEngineWorkerPropertyInfo.kt new file mode 100644 index 0000000..6e5e75c --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/ProcessEngineWorkerPropertyInfo.kt @@ -0,0 +1,11 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +import dev.bpmcrafters.processengine.worker.documentation.api.PropertyType + +data class ProcessEngineWorkerPropertyInfo( + val name: String, + val label: String, + val type: PropertyType, + val editable: Boolean, + val notEmpty: Boolean, +) diff --git a/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/TargetPlattform.kt b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/TargetPlattform.kt new file mode 100644 index 0000000..b8eed43 --- /dev/null +++ b/documentation/generator-api/src/main/kotlin/dev/bpmcrafters/processengine/worker/documentation/generator/api/TargetPlattform.kt @@ -0,0 +1,6 @@ +package dev.bpmcrafters.processengine.worker.documentation.generator.api + +enum class TargetPlattform { + C7, + C8 +} diff --git a/documentation/pom.xml b/documentation/pom.xml new file mode 100644 index 0000000..581ba1d --- /dev/null +++ b/documentation/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + dev.bpm-crafters.process-engine-worker + process-engine-worker-root + 0.6.1-SNAPSHOT + + + process-engine-worker-documentation + ${project.artifactId} + + documentation-api + generator-api + documentation-core + documentation-c8-maven-plugin + documentation-c7-maven-plugin + + pom + + diff --git a/examples/camunda7-documentation-example/README.md b/examples/camunda7-documentation-example/README.md new file mode 100644 index 0000000..254aae8 --- /dev/null +++ b/examples/camunda7-documentation-example/README.md @@ -0,0 +1,7 @@ +# camunda7-documentation-example + +camunda7-documentation-example is a minimal example to demonstrate the generation of documentation (element-templates) for process-engine-workers. + +## Usage + +Build the project with `mvn clean install` and checkout the generated documentation (element-templates) in [src/main/resources/bpmn/](src/main/resources/bpmn). diff --git a/examples/camunda7-documentation-example/pom.xml b/examples/camunda7-documentation-example/pom.xml new file mode 100644 index 0000000..af48a9c --- /dev/null +++ b/examples/camunda7-documentation-example/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + dev.bpm-crafters.process-engine-worker + process-engine-worker-examples + 0.6.1-SNAPSHOT + + + camunda7-documentation-example + + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-core + ${project.version} + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-api + ${project.version} + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + + dev.bpm-crafters.process-engine-worker + process-engine-worker-documentation-c7-maven-plugin + 0.6.1-SNAPSHOT + + true + src/main/resources/bpmn/element-templates + + true + + + + + + generate + + process-classes + + + + + + diff --git a/examples/camunda7-documentation-example/src/main/kotlin/worker/ExampleDto.kt b/examples/camunda7-documentation-example/src/main/kotlin/worker/ExampleDto.kt new file mode 100644 index 0000000..fc96cb9 --- /dev/null +++ b/examples/camunda7-documentation-example/src/main/kotlin/worker/ExampleDto.kt @@ -0,0 +1,9 @@ +package worker + +import dev.bpmcrafters.processengine.worker.documentation.api.ProcessEngineWorkerPropertyDocumentation + +data class ExampleDto( + @field:ProcessEngineWorkerPropertyDocumentation(label = "Example Input") + val exampleInput: String +) { +} diff --git a/examples/camunda7-documentation-example/src/main/kotlin/worker/ExampleWorker.kt b/examples/camunda7-documentation-example/src/main/kotlin/worker/ExampleWorker.kt new file mode 100644 index 0000000..13d40c5 --- /dev/null +++ b/examples/camunda7-documentation-example/src/main/kotlin/worker/ExampleWorker.kt @@ -0,0 +1,14 @@ +package worker + +import dev.bpmcrafters.processengine.worker.ProcessEngineWorker +import dev.bpmcrafters.processengine.worker.Variable +import dev.bpmcrafters.processengine.worker.documentation.api.ProcessEngineWorkerDocumentation + +class ExampleWorker { + + @ProcessEngineWorker("example-worker") + @ProcessEngineWorkerDocumentation(name = "Example Worker", description = "Example worker for documentation generation") + fun execute(@Variable(name = "exampleDto") exampleDto: ExampleDto): ExampleDto { + return exampleDto + } +} diff --git a/examples/camunda7-documentation-example/src/main/resources/bpmn/element-templates/Example-Worker.json b/examples/camunda7-documentation-example/src/main/resources/bpmn/element-templates/Example-Worker.json new file mode 100644 index 0000000..3e933ec --- /dev/null +++ b/examples/camunda7-documentation-example/src/main/resources/bpmn/element-templates/Example-Worker.json @@ -0,0 +1,67 @@ +{ + "$schema" : "https://unpkg.com/@camunda/element-templates-json-schema@0.1.0/resources/schema.json", + "name" : "Example Worker", + "id" : "example-worker", + "appliesTo" : [ "bpmn:ServiceTask" ], + "properties" : [ { + "value" : "external", + "label" : "Implementation Type", + "type" : "String", + "editable" : false, + "binding" : { + "type" : "property", + "name" : "camunda:type" + } + }, { + "value" : "example-worker", + "label" : "Topic", + "type" : "String", + "editable" : false, + "binding" : { + "type" : "property", + "name" : "camunda:topic" + } + }, { + "value" : false, + "label" : "Async Before", + "type" : "Boolean", + "editable" : true, + "binding" : { + "type" : "property", + "name" : "camunda:asyncBefore" + } + }, { + "value" : true, + "label" : "Async After", + "type" : "Boolean", + "editable" : true, + "binding" : { + "type" : "property", + "name" : "camunda:asyncAfter" + } + }, { + "value" : "${}", + "label" : "Input: Example Input", + "type" : "String", + "editable" : true, + "binding" : { + "type" : "camunda:inputParameter", + "name" : "exampleInput" + }, + "constraints" : { + "notEmpty" : false + } + }, { + "value" : "exampleInput", + "label" : "Output: Example Input", + "type" : "String", + "editable" : true, + "binding" : { + "type" : "camunda:outputParameter", + "source" : "${exampleInput}" + }, + "constraints" : { + "notEmpty" : false + } + } ] +} \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index 6005b74..b2c236b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -24,6 +24,7 @@ order-fulfillment camunda7-remote-starter-native camunda7-embedded-starter-transaction + camunda7-documentation-example diff --git a/pom.xml b/pom.xml index 9e6b8dc..275ca6e 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ spring-boot-starter + documentation