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