From 35485b3f49123cab5e45f2a9d2d0f154d6268de2 Mon Sep 17 00:00:00 2001 From: StephenOTT Date: Sat, 9 Nov 2019 15:04:15 -0500 Subject: [PATCH 01/35] initial setup for conversion to micronaut --- .gitignore | 51 +- Dockerfile | 4 + README.md | 709 +----------------- bpmn/bpmn1.bpmn | 77 -- build.gradle | 77 ++ docker/docker-compose.yml | 12 - docs/design/cluster.png | Bin 194515 -> 0 bytes docs/design/dataflow.png | Bin 89022 -> 0 bytes docs/design/designs.graffle | Bin 15660 -> 0 bytes docs/design/form/FormBuilder1-build.png | Bin 39579 -> 0 bytes docs/design/form/FormBuilder2-build.png | Bin 79728 -> 0 bytes docs/design/form/FormBuilder3-build.png | Bin 85155 -> 0 bytes docs/design/form/FormBuilder4-render.png | Bin 18198 -> 0 bytes .../form/User-Task-Form-Completion-Flow.png | Bin 97561 -> 0 bytes gradle.properties | 2 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54413 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 +++++ gradlew.bat | 84 +++ micronaut-cli.yml | 5 + pom.xml | 143 ---- settings.gradle | 1 + .../com/github/stephenott/MainVerticle.java | 210 ------ .../com/github/stephenott/common/Common.java | 7 - .../stephenott/common/EventBusable.java | 11 - .../common/EventBusableMessageCodec.java | 43 -- .../common/EventBusableReplyException.java | 63 -- .../conf/ApplicationConfiguration.java | 501 ------------- .../com/github/stephenott/conf/config.json | 3 - .../stephenott/executors/JobResult.java | 103 --- .../executors/polyglot/ExecutorVerticle.java | 50 -- .../usertask/UserTaskConfiguration.java | 84 --- .../usertask/UserTaskExecutorVerticle.java | 108 --- .../FormValidationServerHttpVerticle.java | 190 ----- .../form/validator/ValidationRequest.java | 35 - .../validator/ValidationRequestResult.java | 208 ----- .../validator/ValidationSchemaObject.java | 47 -- .../validator/ValidationServiceRequest.java | 34 - .../validator/ValidationSubmissionObject.java | 34 - .../InvalidFormSubmissionException.java | 16 - .../ValidationRequestResultException.java | 34 - .../ManagementHttpVerticle.java | 296 -------- .../com/github/stephenott/package-info.java | 4 - .../usertask/CompletionRequest.java | 71 -- .../stephenott/usertask/DbActionResult.java | 32 - .../usertask/FailedDbActionException.java | 27 - .../usertask/FormSchemaService.java | 20 - .../usertask/FormSchemaServiceImpl.java | 4 - .../stephenott/usertask/GetRequest.java | 103 --- .../usertask/GetTasksFormSchemaReqRes.java | 73 -- .../usertask/SubmitTaskComposeDto.java | 73 -- .../usertask/UserTaskActionsVerticle.java | 239 ------ .../usertask/UserTaskHttpServerVerticle.java | 513 ------------- .../usertask/entity/FormSchemaEntity.java | 95 --- .../usertask/entity/UserTaskEntity.java | 279 ------- .../JsonToStringDeserializer.java | 15 - .../usertask/mongo/MongoManager.java | 34 - .../usertask/mongo/Subscribers.java | 198 ----- .../client/CreateInstanceConfiguration.java | 51 -- .../ZeebeClientConfigurationProperties.java | 153 ---- .../zeebe/client/ZeebeClientVerticle.java | 290 ------- .../stephenott/zeebe/dto/ActivatedJobDto.java | 194 ----- .../com/github/stephenott/qtz/Application.kt | 14 + .../qtz/forms/validator/FormSchema.kt | 7 + .../qtz/forms/validator/FormSubmission.kt | 40 + .../qtz/forms/validator/client/FormsClient.kt | 16 + src/main/resources/application.yml | 3 + src/main/resources/logback.xml | 15 + .../java/com/github/stephenott/MyTest.java | 54 -- .../io/kotlintest/provided/ProjectConfig.kt | 9 + zeebe.yml | 57 -- 71 files changed, 466 insertions(+), 5636 deletions(-) create mode 100644 Dockerfile delete mode 100644 bpmn/bpmn1.bpmn create mode 100644 build.gradle delete mode 100644 docker/docker-compose.yml delete mode 100644 docs/design/cluster.png delete mode 100644 docs/design/dataflow.png delete mode 100644 docs/design/designs.graffle delete mode 100644 docs/design/form/FormBuilder1-build.png delete mode 100644 docs/design/form/FormBuilder2-build.png delete mode 100644 docs/design/form/FormBuilder3-build.png delete mode 100644 docs/design/form/FormBuilder4-render.png delete mode 100644 docs/design/form/User-Task-Form-Completion-Flow.png create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100755 gradlew.bat create mode 100644 micronaut-cli.yml delete mode 100644 pom.xml create mode 100644 settings.gradle delete mode 100644 src/main/java/com/github/stephenott/MainVerticle.java delete mode 100644 src/main/java/com/github/stephenott/common/Common.java delete mode 100644 src/main/java/com/github/stephenott/common/EventBusable.java delete mode 100644 src/main/java/com/github/stephenott/common/EventBusableMessageCodec.java delete mode 100644 src/main/java/com/github/stephenott/common/EventBusableReplyException.java delete mode 100644 src/main/java/com/github/stephenott/conf/ApplicationConfiguration.java delete mode 100644 src/main/java/com/github/stephenott/conf/config.json delete mode 100644 src/main/java/com/github/stephenott/executors/JobResult.java delete mode 100644 src/main/java/com/github/stephenott/executors/polyglot/ExecutorVerticle.java delete mode 100644 src/main/java/com/github/stephenott/executors/usertask/UserTaskConfiguration.java delete mode 100644 src/main/java/com/github/stephenott/executors/usertask/UserTaskExecutorVerticle.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/FormValidationServerHttpVerticle.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/ValidationRequest.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/ValidationRequestResult.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/ValidationSchemaObject.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/ValidationServiceRequest.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/ValidationSubmissionObject.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/exception/InvalidFormSubmissionException.java delete mode 100644 src/main/java/com/github/stephenott/form/validator/exception/ValidationRequestResultException.java delete mode 100644 src/main/java/com/github/stephenott/managementserver/ManagementHttpVerticle.java delete mode 100644 src/main/java/com/github/stephenott/package-info.java delete mode 100644 src/main/java/com/github/stephenott/usertask/CompletionRequest.java delete mode 100644 src/main/java/com/github/stephenott/usertask/DbActionResult.java delete mode 100644 src/main/java/com/github/stephenott/usertask/FailedDbActionException.java delete mode 100644 src/main/java/com/github/stephenott/usertask/FormSchemaService.java delete mode 100644 src/main/java/com/github/stephenott/usertask/FormSchemaServiceImpl.java delete mode 100644 src/main/java/com/github/stephenott/usertask/GetRequest.java delete mode 100644 src/main/java/com/github/stephenott/usertask/GetTasksFormSchemaReqRes.java delete mode 100644 src/main/java/com/github/stephenott/usertask/SubmitTaskComposeDto.java delete mode 100644 src/main/java/com/github/stephenott/usertask/UserTaskActionsVerticle.java delete mode 100644 src/main/java/com/github/stephenott/usertask/UserTaskHttpServerVerticle.java delete mode 100644 src/main/java/com/github/stephenott/usertask/entity/FormSchemaEntity.java delete mode 100644 src/main/java/com/github/stephenott/usertask/entity/UserTaskEntity.java delete mode 100644 src/main/java/com/github/stephenott/usertask/json/deserializer/JsonToStringDeserializer.java delete mode 100644 src/main/java/com/github/stephenott/usertask/mongo/MongoManager.java delete mode 100644 src/main/java/com/github/stephenott/usertask/mongo/Subscribers.java delete mode 100644 src/main/java/com/github/stephenott/zeebe/client/CreateInstanceConfiguration.java delete mode 100644 src/main/java/com/github/stephenott/zeebe/client/ZeebeClientConfigurationProperties.java delete mode 100644 src/main/java/com/github/stephenott/zeebe/client/ZeebeClientVerticle.java delete mode 100644 src/main/java/com/github/stephenott/zeebe/dto/ActivatedJobDto.java create mode 100644 src/main/kotlin/com/github/stephenott/qtz/Application.kt create mode 100644 src/main/kotlin/com/github/stephenott/qtz/forms/validator/FormSchema.kt create mode 100644 src/main/kotlin/com/github/stephenott/qtz/forms/validator/FormSubmission.kt create mode 100644 src/main/kotlin/com/github/stephenott/qtz/forms/validator/client/FormsClient.kt create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/logback.xml delete mode 100644 src/test/java/com/github/stephenott/MyTest.java create mode 100644 src/test/kotlin/io/kotlintest/provided/ProjectConfig.kt delete mode 100644 zeebe.yml diff --git a/.gitignore b/.gitignore index ed80467..c69d1ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,13 @@ -# Created by .ignore support plugin (hsz.mobi) -### Maven template +Thumbs.db +.DS_Store +.gradle +build/ target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties -.mvn/wrapper/maven-wrapper.jar - -### Java template -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -.idea/* +out/ +.idea *.iml - -tmp/* \ No newline at end of file +*.ipr +*.iws +.project +.settings +.classpath \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2097541 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM adoptopenjdk/openjdk11-openj9:jdk-11.0.1.13-alpine-slim +COPY build/libs/quintessential-tasklist-zeebe-*-all.jar quintessential-tasklist-zeebe.jar +EXPOSE 8080 +CMD java -Dcom.sun.management.jmxremote -noverify ${JAVA_OPTS} -jar quintessential-tasklist-zeebe.jar \ No newline at end of file diff --git a/README.md b/README.md index e8a6fa0..68e2f09 100644 --- a/README.md +++ b/README.md @@ -1,711 +1,4 @@ # quintessential-tasklist-zeebe The quintessential Zeebe tasklist for BPMN Human tasks with Drag and Drop Form builder, client and server side validations, and drop in Form Rendering -WIP - -Setup SLF4J logging: `-Dvertx.logger-delegate-factory-class-name=io.vertx.core.logging.SLF4JLogDelegateFactory` - -vertx run command: `run com.github.stephenott.MainVerticle -conf src/main/java/com/github/stephenott/conf/conf.json` - -Current Zeebe Version: `0.21.0-alpha1` -Current Vertx Version: `3.8.0` -Java: `1.8` - - -# Cluster Architecture - -![cluster-arch](./docs/design/cluster.png) - -- Clients, Workers, and Executors can be added at startup and during runtime. -- Failed nodes in the Vertx Cluster (Clients, Workers, and Executors) will be re-instantiated through the vertx cluster manager's configuration. - - -# Form Building UI - -The Form Builder UI uses Formio.js as the Builder and Render. -The schema that was generated from the builder is persisted and used during the User Task Submission with Form flow. - -![builder1](./docs/design/form/FormBuilder1-build.png) - -![builder2](./docs/design/form/FormBuilder2-build.png) - -![builder3](./docs/design/form/FormBuilder3-build.png) - - -And then you can render and make a submission: - -![builder4](./docs/design/form/FormBuilder4-render.png) - - -Try out the builder on: https://formio.github.io/formio.js/app/builder - - -## User Task Submission with Form Data flow - -![dataflow](./docs/design/form/User-Task-Form-Completion-Flow.png) - - -# ZeebeClient/Worker/Executor Data Flow - -![data flow](./docs/design/dataflow.png) - - -# Configuration - -Extensive configuration capabilities are provided to control the exact setup of your application: - -The Yaml location can be configured through the applications config.json. Default is `./zeebe.yml`. - -Example: - -```yaml -zeebe: - clients: - - name: MyCustomClient - brokerContactPoint: "localhost:25600" - requestTimeout: PT20S - workers: - - name: SimpleScriptWorker - jobTypes: - - type1 - timeout: PT10S - - name: UT-Worker - jobTypes: - - ut.generic - timeout: P1D - -executors: - - name: Script-Executor - address: "type1" - execute: ./scripts/script1.js - - name: CommonGenericExecutor - address: commonExecutor - execute: classpath:com.custom.executors.Executor1 - - name: IpBlocker - address: block-ip - execute: ./cyber/BlockIP.py - -userTaskExecutors: - - name: GenericUserTask - address: ut.generic - -managementServer: - enabled: true - apiRoot: server1 - corsRegex: ".*." - port: 8080 - instances: 1 - zeebeClient: - name: DeploymentClient - brokerContactPoint: "localhost:25600" - requestTimeout: PT10S - -formValidatorServer: - enabled: true - corsRegex: ".*." - port: 8082 - instances: 1 - formValidatorService: - host: localhost - port: 8083 - validateUri: /validate - requestTimeout: 5000 - -userTaskServer: - enabled: true - corsRegex: ".*." - port: 8080 - instances: 1 -``` - -# Zeebe Clients - -A Zeebe Client is a gRPC channel to a specific Zeebe Cluster. - -A client maintains a set of "Job Workers", which are long polling the Zeebe Cluster for Zeebe Jobs that have a `type` listed in the `jobTypes` array. - -Zeebe Clients have the following configuration: - -```yaml -zeebe: - clients: - - name: MyCustomClient - brokerContactPoint: "localhost:25600" - requestTimeout: PT20S - workers: - - name: SimpleScriptWorker - jobTypes: - - type1 - timeout: PT10S - - name: UT-Worker - jobTypes: - - ut.generic - timeout: P1D -``` - -Where `name` is the name of the client. The same name could be used by multiple clients in the same server or by other servers. The `name` is used as the `zeebeSource` in Executors and User Task Executors as the source system to send completed/failed Zeebe Jobs back to. - -Where `workers` is a array of Zeebe Worker definitions. A worker definition has a `name` and a list of `jobTypes`. - -`name` is the worker name that is provided to Zeebe as the worker that requested the job. - -`jobTypes` is the lsit of Zeebe job `types` that will be queried for using long polling. - -Jobs that are retrieved will be routed to the event bus using the address: `job.:jobType:`, where `:jobType:` is the specific Zeebe job's `type` property. -Make sure you have executors (Polyglot, User Task or custom) on the network connected to the vertx cluster or else the job will not be consumed by a worker. - - Take note of the usage of the `timeout` which is the deadline that the job will be locked for. - The usage has special applicability for Jobs that you want to use with User Task; where you will want to set the timeout as a much longer period than a typical executor. - - -# Executors - -Executors provide a polyglot execution solution for completing Zeebe Jobs. - -Executors have the following configuration: -Example of three different executors: - -```yaml -executors: - - name: Script-Executor - address: "type1" - execute: ./scripts/script1.js - instances: 2 - - name: CommonGenericExecutor - address: commonExecutor - execute: classpath:com.custom.executors.Executor1 - - name: IpBlocker - address: block-ip - execute: ./cyber/BlockIP.py -``` - -Executors can execute scripts and classes as defined in the executor's polyglot capabilities. - -Where `address` is the Zeebe job `type` that would be configured in the task in the BPMN. - -Where `execute` is the class/script that will be executor when jobs are sent to this executor - -Where `name` is the unique name of the Executor used for logging purposes. - -You can deploy a Executor with multiple `instances` to provide more more parallel throughput capacity. - -Required properties: `name`, `address`, `execute` - -Completion of Jobs sent to Executors is captured over the event bus with the JobResult object. -Completed (successfully or a failure such as a business error) are sent as a JobResult to event bus address: `sourceClient.job-aciton.completion`. - -Where `sourceClient` is the ZeebeClient `name` that is used in the `zeebe.clients[].name` property. - -The `sourceClient` ensures that a completed job can be sent back to the same Zeebe Cluster, but not necessarily using the same instance of a ZeebeClient that consumed the job. - -JobResult's that have a `result=FAIL` will have their corresponding Zeebe Job actioned as a Failed Job. - - -# User Task Executors - -User Task(UT) Executors are a special type of executor that are dedicated to the logic handling of BPMN User Tasks. - -UT Executors have the following configuration: - -```yaml -userTaskExecutors: - - name: GenericUserTask - address: ut.generic - instances: 1 -``` - -Required properties: `name`, `address` - -Where `address` is the Zeebe job `type` that would be configured in the task in the BPMN. - -Internally executors have their addresses prefixed with a common job prefix to ensure proper message namespacing. - -Where `name` is the unique name of the UT Executor used for logging purposes. - -You can deploy a UT Executor with multiple `instances` to provide more more parallel throughput capacity. - -UT Executors primary function is to provide capture of UTs from Zeebe and convert the Zeebe jobs into a UserTaskEntity. -A UserTaskEntity is then saved in the storage of choice (such as a DB). - -Completion of User Tasks is captured over the event bus with the JobResult object. - -Completed (successfully or a failure such as a business error) are sent as a JobResult to event bus address: `sourceClient.job-aciton.completion`. - -Where `sourceClient` is the ZeebeClient `name` that is used in the `zeebe.clients[].name` property. - -The `sourceClient` ensures that a completed job can be sent back to the same Zeebe Cluster, but not necessarily using the same instance of a ZeebeClient that consumed the job. - -JobResult's that have a `result=FAIL` will have their corresponding Zeebe Job actioned as a Failed Job. - - -## User Tasks - -The default build of User Tasks seeks to provide a duplicate or similar User Task experience as Camunda's User Tasks implementation. - -See UserTaskEntity.class, UserTaskConfiguration.class for more details. - -A User Task can be configured in the Zeebe BPMN using custom headers. The supported headers are: - -|key|value|description| -|------|------|----------| -|title|`string`|The title of the task. Can be any string value that will be interpreted by the User Task storage system.| -|description|`string` |The description of the task. Can be any string value that will be interpreted by the User Task storage system.| -|priority|`int`|defaults to 0| -|assignee|`string`|The default assignee of the task. A single value.| -|candidateGroups|`string`|The list of groups that are candidates to claim this task. Comma separated list of strings. Example: `"cg1, cg2, cg3"`| -|candidateUsers|`string`|The list of users that are candidates to claim this task. Comma separated list of strings. Example: `"cu1, cu2, cu3"`| -|dueDate|`string`|The date on which the User Task is due. ISO8601 format| -|formKey|`string`|A value that represents the specific form that should be used by the user when completing this task.| - - -When generating a UserTaskEntity, some additional properties are stored for usage and indexing and convenience: - -In addition to the custom header values above, the following is stored in the UserTaskEntity: - -|key|value|description| -|------|------|----------| -|taskId|`string`|The unique ID of the task. Typically will be a business centric key defined during configuration. If not ID is provided then defaults to `user-task--:UUID:` where `:UUID:` is a random UUID.| -|zeebeSource|`string`|The source ZeebeClient `name` that the ZeebeJob was retrieved from. -|zeebeDeadline|`instant`|The Zeebe Job deadline property| -|zeebeJobKey|`long`|The unique job ID of the Zeebe Job.| -|bpmnProcessId|`string`|The BPMN Process Definition ID| -|zeebeVariables|`Map of String:Object`|The variables from the Zeebe Job| -|metadata|`Map of String:Object`|A generic data holder for additional User Task metadata| - -# Form Validation Server - -The Form Validation Server provides HTTP endpoints for validation a Form Submission based on a provided Form Schema. - -Configuration: - -```yaml -formValidatorServer: - enabled: true - corsRegex: ".*." - port: 8082 - instances: 1 - formValidatorService: - host: localhost - port: 8083 - validateUri: /validate - requestTimeout: 5000 -``` - -Where `formValidatorService` is the Form Validator service that performs the actual form validation. - -Example Validation Request: - -POST: `localhost:8083/validate` - -Body: - -```json -{ - "schema":{ - "display": "form", - "components": [ - { - "label": "Text Field", - "allowMultipleMasks": false, - "showWordCount": false, - "showCharCount": false, - "tableView": true, - "alwaysEnabled": false, - "type": "textfield", - "input": true, - "key": "textField2", - "defaultValue": "", - "validate": { - "customMessage": "", - "json": "", - "required": true - }, - "conditional": { - "show": "", - "when": "", - "json": "" - }, - "inputFormat": "plain", - "encrypted": false, - "properties": {}, - "customConditional": "", - "logic": [], - "attributes": {}, - "widget": { - "type": "" - }, - "reorder": false - }, - { - "type": "button", - "label": "Submit", - "key": "submit", - "disableOnInvalid": true, - "theme": "primary", - "input": true, - "tableView": true - } - ], - "settings": { - } - }, - "submission":{ - "data": { - "textField2": 123, - "dog": "cat" - }, - "metadata": {} -} -} -``` - -Response if validation passes: - -```json -{ - "processed_submission": { - "textField2": "sog" - } -} -``` - -Notice that the extra `dog` property is removed because it is not a valid field in the form schema. - -Response if validation fails: - -```json -{ - "isJoi": true, - "name": "ValidationError", - "details": [ - { - "message": "\"textField2\" must be a string", - "path": "textField2", - "type": "string.base", - "context": { - "value": 123, - "key": "textField2", - "label": "textField2" - } - } - ], - "_object": { - "textField2": 123, - "dog": "cat" - }, - "_validated": { - "textField2": 123 - } -} -``` - -The validation service is also available over the event bus at the `address` property defined in the Form Validation Server configuration. - - -# Management Server - -The management server provides HTTP endpoints for working with Zeebe clusters - -Configuration: - -```yaml -managementServer: - enabled: true - apiRoot: server1 - corsRegex: ".*." - port: 8080 - zeebeClient: - name: DeploymentClient - brokerContactPoint: "localhost:25600" - requestTimeout: PT10S - instances: 1 - fileUploadPath: ./tmp/uploads -``` - -required fields: `apiRoot`, `zeebeClient` - -`apiRoot` must be unique. - -## Deploy Workflow - -`POST localhost:8080/server1/deploy` - -Headers: -- `Content-Type: multipart/form-data` - -form-data: -- file name (must be a .bpmn or .yaml file) : file upload (the binary file you are uploading such as a .bpmn file) - -Where `server1` is the `apiRoot` value defined in the YAML configuration. - -You can deploy many management servers as needed. Each server can be deployed for different zeebe clusters. - -You can deploy the same server with multiple `instances` to provide more throughput. - -## Create Workflow Instance / Start Workflow - -`POST localhost:8080/server1/create-instance` - -Headers: -- `Content-Type: application/json` -- `Accept: application/json` - -Json Body: - -```json -{ - "workflowKey": 1234567890 -} -``` - -Where `workflowKey` is the unique workflow key that was generated for the BPMN process/pool during deployment. - -You may also use: - -```json -{ - "bpmnProcessId": "myProcess", - "bpmnProcessVersion": 2 -} -``` - -Where `bpmnProcessId` is the BPMN's process Id property (sometimes referred to as a process key). -The `bpmnProcessVersion` is optional. You can set the version number or set as `-1` which means "latest version" / newest. If you do not provide the property it will default to latest version. - -`varaibles` can also be provided as a json object: - -```json -{ - "workflowKey": 1234567890, - "variables": { - "myVar1": 123, - "myVar2": "some value", - "myVarABC": [1,2,5,10], - "myVarXYZ": { - "1": "A", - "2": "B" - } - } -} -``` - -The variables will be injected into the created workflow instance. - - -# User Task Server - -A User Task HTTP server that provides User Task persistence, querying, completion, etc. - -The server also provides a Form Schema Entity persistence, querying, and validation of submissions against the schema. -The Form Schema is what will be submitted to the Form Validator Service. - -## Server Configuration - -```yaml -userTaskServer: - enabled: true - corsRegex: ".*." - port: 8080 - instances: 1 -``` - -## Actions: - -1. Save Form Schema -1. Complete User Task -1. Get User Tasks -1. Submit Form to Complete a User Task -1. Delete User Task (TODO) -1. Claim User Task (TODO) -1. UnClaim User Task (TODO) -1. Assign User Task (TODO) -1. Create Custom User Task (not linked to Zeebe Job) - -## Save Form Schema - -POST `/form/schema` - -```json -{ - "owner": "Department-1", - "key": "MySimpleForm1", - "title": "My Simple Form 1", - "schema": { - "display": "form", - "components": [ - { - "label": "Text Field", - "allowMultipleMasks": false, - "showWordCount": false, - "showCharCount": false, - "tableView": true, - "alwaysEnabled": false, - "type": "textfield", - "input": true, - "key": "textField2", - "defaultValue": "", - "validate": { - "customMessage": "", - "json": "", - "required": true - }, - "conditional": { - "show": "", - "when": "", - "json": "" - }, - "inputFormat": "plain", - "encrypted": false, - "properties": {}, - "customConditional": "", - "logic": [], - "attributes": {}, - "widget": { - "type": "" - }, - "reorder": false - }, - { - "type": "button", - "label": "Submit", - "key": "submit", - "disableOnInvalid": true, - "theme": "primary", - "input": true, - "tableView": true - } - ], - "settings": { - } - } -} -``` - -The `key` property is the `formKey` value you setup in your zeebe task custom headers. - -Required fields: `owner`, `key`, `title`, `schema` - - -## Complete User Task - -Mainly used as a administrative endpoint to complete a User Task without any Form - -POST `/task/complete` - -```json -{ - "job": 2251799813685292, - "source": "MyCustomClient", - "completionVariables": {} -} -``` - -`Source` is the zeebe client name configured in your configuration yaml. - - -## Get Tasks - -GET `/task` - -JSON Body: - -Query is run as a `AND` query on each of the arguments - -```json -{ - "taskId": "", - "state": "", - "title": "", - "assignee": "", - "dueDate": "", - "zeebeJobKey": "", - "zeebeSource": "", - "bpmnProcessId": "" -} -``` - -You can pass `{}` as the body if you want to return all User Tasks. - - -## Submit Task with Form - -POST `/task/id/:taskId/submit` - -Example: `localhost:8088/task/id/user-task--080946c6-1355-4cd7-9fcf-86fc9c46d4c4/submit` - -Json Body: - -```json -{ - "data": { - "textField2": "sog", - "dog": "cat" - }, - "metadata": {} -} -``` - -This endpoint acts the same as the Validation Server's `/validate` endpoint. The difference is the User Task's endpoint will complete the User Task entity in the DB if the form is valid, and the Form fields will be saved in the Zeebe workflow as variables when the Job is completed. - -Upon successful form validation, and assuming the User Task is not already completed, then the User Task will be made complete and the completion variables will be saved. -Then a background worker is watching for completed user tasks and will attempt to report this back to the Zeebe Job. -The behaviour is this way so you can complete User Tasks without having to have a active connection to the Zeebe Cluster. - -## Delete User Task - -TODO... - - -## Claim User Task - -TODO... - -## UnClaim User Task - -TODO... - -## Assign User Task - -TODO... - -## Create Custom User Task (not backed by Zeebe Job) - -TODO... - ----- - -# Raw Notes - -1. Implements clustering and scaling through Vertx instances. -1. ZeebeClientVerticle can increase in number of instances: 1 instance == 1 ZeebeClient Channel connection. -1. ExecutorVerticle is independent of ZeebeClient. You can scale executors across the cluster to any number of instances and have full cluster feature set. -1. a JobResult is what holds the context of if a Zeebe Failure should occur in the context of the actual Work that a executor preformed. -1. Management HTTP takes a apiRoot namespace which is the prefix for the api calls to deploy and start process instances -1. TODO: Add a send message HTTP verticle -1. UserTaskConfiguration is the data that is received from a Zeebe Custom Headers which is used to generate the Entity -1. UserTaskVerticle is a example of a custom worker. I have made them individual so User Tasks can be managed as stand along systems -1. The Client Name property of the Client config is used as the EB address namespace for sending job completions back over the wire -1. TODO move executeBlocking code into their own worker verticles with their own thread pools -1. sourceClient is passed over the wire as a header which represents the client Name. The client name is the client (or any instance of that name/id) that is used to send back into zeebe. This supports multiple clients to different brokers (representing tenants for data separation) -1. Breakers needs to be added -1. Polling for Jobs is a executeBlocking action. When polling is complete (found jobs or did not find jobs), it will call the poll jobs again. It assumes long polling is enabled. -1. TODO review defaults and setup of entity build in the user task verticle as its very messy right now. -1. Management Server uses the route namespacing because it is assumed that security will be added by a proxy in the network. If app level security needs to be added, then the ManagementHttpVerticle can be easily copied and replaced with security logic. -1. TODO move EB addresses in a global static class for easy global management -1. TODO fix up the logging to be DEBUG and cleanup the language as the standard is all over the place at the moment. Also inlcude more context info for when reading the log as its unclear. -1. TODO ***** Add the defaults logic for the User Task assignments, where if the headers that are not provided in zeebe then the user tasks entity will default to those configured values. -1. TODO add the overrides logic: where if a override is provided then only the logic from the override is used and the provided header does not matter -1. TODO Refactor error handling on HTTP requests to provider better json errors - -```xml -... - - - - - - - - -... -``` +KOTLIN \ No newline at end of file diff --git a/bpmn/bpmn1.bpmn b/bpmn/bpmn1.bpmn deleted file mode 100644 index d04a9bf..0000000 --- a/bpmn/bpmn1.bpmn +++ /dev/null @@ -1,77 +0,0 @@ - - - - - SequenceFlow_0mi3b9p - - - - - - - - - - - - SequenceFlow_0z6yvdy - SequenceFlow_0zyd6q2 - - - SequenceFlow_0zyd6q2 - - - - - - - SequenceFlow_0mi3b9p - SequenceFlow_1wzqads - - - - - - - - SequenceFlow_1wzqads - SequenceFlow_0z6yvdy - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..de0223a --- /dev/null +++ b/build.gradle @@ -0,0 +1,77 @@ +plugins { + id "org.jetbrains.kotlin.jvm" version "1.3.50" + id "org.jetbrains.kotlin.kapt" version "1.3.50" + id "org.jetbrains.kotlin.plugin.allopen" version "1.3.50" + id "com.github.johnrengelman.shadow" version "5.0.0" + id "application" +} + + + +version "0.1" +group "quintessential.tasklist.zeebe" + +repositories { + mavenCentral() + maven { url "https://jcenter.bintray.com" } +} + +configurations { + // for dependencies that are needed for development only + developmentOnly +} + +dependencies { + implementation platform("io.micronaut:micronaut-bom:$micronautVersion") + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}" + implementation "org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}" + implementation "io.micronaut:micronaut-runtime" + implementation "io.micronaut:micronaut-http-server-netty" + implementation "io.micronaut:micronaut-http-client" + implementation 'io.micronaut:micronaut-validation' + kapt platform("io.micronaut:micronaut-bom:$micronautVersion") + kapt "io.micronaut:micronaut-inject-java" + kapt "io.micronaut:micronaut-validation" + kaptTest platform("io.micronaut:micronaut-bom:$micronautVersion") + kaptTest "io.micronaut:micronaut-inject-java" + runtimeOnly "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8" + runtimeOnly "ch.qos.logback:logback-classic:1.2.3" + testImplementation platform("io.micronaut:micronaut-bom:$micronautVersion") + testImplementation "io.micronaut.test:micronaut-test-kotlintest" + testImplementation "io.mockk:mockk:1.9.3" + testImplementation "io.kotlintest:kotlintest-runner-junit5:3.3.2" +} + +test.classpath += configurations.developmentOnly + +mainClassName = "com.github.stephenott.qtz.Application" + +test { + useJUnitPlatform() +} + +allOpen { + annotation("io.micronaut.aop.Around") +} + +compileKotlin { + kotlinOptions { + jvmTarget = '1.8' + //Will retain parameter names for Java reflection + javaParameters = true + } +} + +compileTestKotlin { + kotlinOptions { + jvmTarget = '1.8' + javaParameters = true + } +} + +shadowJar { + mergeServiceFiles() +} + +run.classpath += configurations.developmentOnly +run.jvmArgs('-noverify', '-XX:TieredStopAtLevel=1', '-Dcom.sun.management.jmxremote') diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml deleted file mode 100644 index c5ff8fd..0000000 --- a/docker/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: "2" - -services: - zeebe: - restart: always - container_name: zeebe_broker - image: camunda/zeebe:0.20.0 - environment: - - ZEEBE_LOG_LEVEL=info - ports: - - "26500:26500" - - "9600:9600" \ No newline at end of file diff --git a/docs/design/cluster.png b/docs/design/cluster.png deleted file mode 100644 index 250c9ff6355626b80d06c4a52296294209aaaa9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 194515 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