From 172117aa0b7cd310ba98e5fb10fab461afe3f616 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Mon, 5 Jul 2021 15:05:51 +0200 Subject: [PATCH 01/27] Update for next development version --- bom/datapool-dependencies/pom.xml | 2 +- bom/parent/pom.xml | 2 +- bom/taskpool-dependencies/pom.xml | 4 ++-- core/datapool/datapool-api/pom.xml | 2 +- core/datapool/datapool-core/pom.xml | 2 +- core/datapool/datapool-event/pom.xml | 2 +- core/datapool/pom.xml | 2 +- core/taskpool/pom.xml | 2 +- core/taskpool/taskpool-api/pom.xml | 2 +- core/taskpool/taskpool-core/pom.xml | 2 +- core/taskpool/taskpool-event/pom.xml | 2 +- examples/components/process-backend/pom.xml | 2 +- examples/components/process-forms/pom.xml | 2 +- examples/components/tasklist-angular/pom.xml | 2 +- examples/components/tasklist-backend/pom.xml | 2 +- examples/components/tasklist-reactive-backend/pom.xml | 2 +- examples/components/users/pom.xml | 2 +- examples/pom.xml | 2 +- .../cockpit-application/application/pom.xml | 2 +- .../cockpit-application/backend/pom.xml | 2 +- .../cockpit-application/frontend/pom.xml | 2 +- .../distributed-axon-server/cockpit-application/pom.xml | 2 +- examples/scenarios/distributed-axon-server/pom.xml | 2 +- .../distributed-axon-server/process-application/pom.xml | 2 +- .../distributed-axon-server/taskpool-application/pom.xml | 2 +- examples/scenarios/pom.xml | 2 +- examples/scenarios/single-node/pom.xml | 2 +- integration/camunda-bpm/engine-client/pom.xml | 2 +- integration/camunda-bpm/pom.xml | 2 +- integration/camunda-bpm/springboot-starter/pom.xml | 2 +- integration/camunda-bpm/taskpool-collector/pom.xml | 2 +- integration/common/datapool-sender/pom.xml | 2 +- integration/common/pom.xml | 2 +- integration/common/tasklist-url-resolver/pom.xml | 2 +- integration/common/taskpool-sender/pom.xml | 2 +- integration/common/variable-serializer/pom.xml | 2 +- pom.xml | 2 +- view/form-url-resolver/pom.xml | 2 +- view/mongo/pom.xml | 2 +- view/pom.xml | 2 +- view/simple/pom.xml | 2 +- view/view-api/pom.xml | 2 +- 42 files changed, 43 insertions(+), 43 deletions(-) diff --git a/bom/datapool-dependencies/pom.xml b/bom/datapool-dependencies/pom.xml index 862987620..4add9d6c9 100644 --- a/bom/datapool-dependencies/pom.xml +++ b/bom/datapool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.0.2 + 3.0.3-SNAPSHOT ../parent/pom.xml diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index db6538cb0..f5481859c 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-root - 3.0.2 + 3.0.3-SNAPSHOT ../../pom.xml diff --git a/bom/taskpool-dependencies/pom.xml b/bom/taskpool-dependencies/pom.xml index d9d4ab7a0..276bfce39 100644 --- a/bom/taskpool-dependencies/pom.xml +++ b/bom/taskpool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.0.2 + 3.0.3-SNAPSHOT ../parent/pom.xml @@ -42,7 +42,7 @@ io.holunda.polyflow polyflow-camunda-bpm-taskpool-collector - 3.0.2 + 3.0.3-SNAPSHOT io.holunda.polyflow diff --git a/core/datapool/datapool-api/pom.xml b/core/datapool/datapool-api/pom.xml index a4a7e561f..c5ff64f6a 100755 --- a/core/datapool/datapool-api/pom.xml +++ b/core/datapool/datapool-api/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-datapool-api diff --git a/core/datapool/datapool-core/pom.xml b/core/datapool/datapool-core/pom.xml index 75d6fe619..3c41760c7 100755 --- a/core/datapool/datapool-core/pom.xml +++ b/core/datapool/datapool-core/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-datapool-core diff --git a/core/datapool/datapool-event/pom.xml b/core/datapool/datapool-event/pom.xml index e6b401c43..273512983 100755 --- a/core/datapool/datapool-event/pom.xml +++ b/core/datapool/datapool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-datapool-event diff --git a/core/datapool/pom.xml b/core/datapool/pom.xml index ceff2360d..5394303e7 100755 --- a/core/datapool/pom.xml +++ b/core/datapool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../bom/parent/pom.xml diff --git a/core/taskpool/pom.xml b/core/taskpool/pom.xml index 8dde2fd03..e2be57be0 100755 --- a/core/taskpool/pom.xml +++ b/core/taskpool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../bom/parent/pom.xml diff --git a/core/taskpool/taskpool-api/pom.xml b/core/taskpool/taskpool-api/pom.xml index af8a1f836..c5d2e9fdd 100755 --- a/core/taskpool/taskpool-api/pom.xml +++ b/core/taskpool/taskpool-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-taskpool-api diff --git a/core/taskpool/taskpool-core/pom.xml b/core/taskpool/taskpool-core/pom.xml index 1eff41765..92fc648cd 100755 --- a/core/taskpool/taskpool-core/pom.xml +++ b/core/taskpool/taskpool-core/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-taskpool-core diff --git a/core/taskpool/taskpool-event/pom.xml b/core/taskpool/taskpool-event/pom.xml index e1d08a661..d8064b1f8 100755 --- a/core/taskpool/taskpool-event/pom.xml +++ b/core/taskpool/taskpool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-taskpool-event diff --git a/examples/components/process-backend/pom.xml b/examples/components/process-backend/pom.xml index ecd5c70e0..bd4255063 100755 --- a/examples/components/process-backend/pom.xml +++ b/examples/components/process-backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../pom.xml diff --git a/examples/components/process-forms/pom.xml b/examples/components/process-forms/pom.xml index 31bb8f133..699f955ff 100755 --- a/examples/components/process-forms/pom.xml +++ b/examples/components/process-forms/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../pom.xml diff --git a/examples/components/tasklist-angular/pom.xml b/examples/components/tasklist-angular/pom.xml index a3e389a8d..38b76216b 100755 --- a/examples/components/tasklist-angular/pom.xml +++ b/examples/components/tasklist-angular/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../pom.xml diff --git a/examples/components/tasklist-backend/pom.xml b/examples/components/tasklist-backend/pom.xml index a27ff9f5b..e4e7e0c46 100755 --- a/examples/components/tasklist-backend/pom.xml +++ b/examples/components/tasklist-backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../pom.xml diff --git a/examples/components/tasklist-reactive-backend/pom.xml b/examples/components/tasklist-reactive-backend/pom.xml index fa6b29000..d63efef15 100755 --- a/examples/components/tasklist-reactive-backend/pom.xml +++ b/examples/components/tasklist-reactive-backend/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../pom.xml diff --git a/examples/components/users/pom.xml b/examples/components/users/pom.xml index 0e9f4ddc0..a1ee10a9f 100644 --- a/examples/components/users/pom.xml +++ b/examples/components/users/pom.xml @@ -7,7 +7,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../pom.xml diff --git a/examples/pom.xml b/examples/pom.xml index 8367aeaa2..bb41c897b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-root - 3.0.2 + 3.0.3-SNAPSHOT ../pom.xml diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml index 6cc8d264f..16127b71a 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.0.2 + 3.0.3-SNAPSHOT polyflow-example-cockpit diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml index 52840e956..c04ff5e12 100644 --- a/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.0.2 + 3.0.3-SNAPSHOT polyflow-example-cockpit-backend diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml index 30d9d9eec..ce9b8e26b 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.0.2 + 3.0.3-SNAPSHOT polyflow-example-cockpit-frontend diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml index 71e5c5f1a..a2d337165 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.0.2 + 3.0.3-SNAPSHOT polyflow-example-cockpit-root diff --git a/examples/scenarios/distributed-axon-server/pom.xml b/examples/scenarios/distributed-axon-server/pom.xml index 46361d2ef..c7d656132 100755 --- a/examples/scenarios/distributed-axon-server/pom.xml +++ b/examples/scenarios/distributed-axon-server/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-root - 3.0.2 + 3.0.3-SNAPSHOT polyflow-example-scenario-distributed-axon-server diff --git a/examples/scenarios/distributed-axon-server/process-application/pom.xml b/examples/scenarios/distributed-axon-server/process-application/pom.xml index ab32ad55e..e08eb81e8 100755 --- a/examples/scenarios/distributed-axon-server/process-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/process-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.0.2 + 3.0.3-SNAPSHOT example-distributed-axon-server-process-application diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml index 6e4d0097b..c1e0c254c 100755 --- a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.0.2 + 3.0.3-SNAPSHOT example-distributed-axon-server-taskpool-application diff --git a/examples/scenarios/pom.xml b/examples/scenarios/pom.xml index 6a240c4bb..d71cd074c 100755 --- a/examples/scenarios/pom.xml +++ b/examples/scenarios/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-example-scenario-root diff --git a/examples/scenarios/single-node/pom.xml b/examples/scenarios/single-node/pom.xml index b5ad76dbe..fb909b5f7 100755 --- a/examples/scenarios/single-node/pom.xml +++ b/examples/scenarios/single-node/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-root - 3.0.2 + 3.0.3-SNAPSHOT polyflow-example-scenario-single-node diff --git a/integration/camunda-bpm/engine-client/pom.xml b/integration/camunda-bpm/engine-client/pom.xml index aa48325f6..170fed453 100644 --- a/integration/camunda-bpm/engine-client/pom.xml +++ b/integration/camunda-bpm/engine-client/pom.xml @@ -4,7 +4,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-camunda-bpm-engine-client diff --git a/integration/camunda-bpm/pom.xml b/integration/camunda-bpm/pom.xml index 6c015dea5..3c8127f34 100644 --- a/integration/camunda-bpm/pom.xml +++ b/integration/camunda-bpm/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../bom/parent/pom.xml diff --git a/integration/camunda-bpm/springboot-starter/pom.xml b/integration/camunda-bpm/springboot-starter/pom.xml index cd629cdb1..4a05e7c2e 100755 --- a/integration/camunda-bpm/springboot-starter/pom.xml +++ b/integration/camunda-bpm/springboot-starter/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-camunda-bpm-springboot-starter diff --git a/integration/camunda-bpm/taskpool-collector/pom.xml b/integration/camunda-bpm/taskpool-collector/pom.xml index a9dda7ee9..83ebc2e4a 100755 --- a/integration/camunda-bpm/taskpool-collector/pom.xml +++ b/integration/camunda-bpm/taskpool-collector/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-camunda-bpm-taskpool-collector diff --git a/integration/common/datapool-sender/pom.xml b/integration/common/datapool-sender/pom.xml index e7c505e0a..d5a40e114 100755 --- a/integration/common/datapool-sender/pom.xml +++ b/integration/common/datapool-sender/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-datapool-sender diff --git a/integration/common/pom.xml b/integration/common/pom.xml index ca741baa0..e5496527b 100755 --- a/integration/common/pom.xml +++ b/integration/common/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.2 + 3.0.3-SNAPSHOT ../../bom/parent/pom.xml diff --git a/integration/common/tasklist-url-resolver/pom.xml b/integration/common/tasklist-url-resolver/pom.xml index c96097d5d..34b5973f3 100644 --- a/integration/common/tasklist-url-resolver/pom.xml +++ b/integration/common/tasklist-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-tasklist-url-resolver diff --git a/integration/common/taskpool-sender/pom.xml b/integration/common/taskpool-sender/pom.xml index c1311ed47..9227da71b 100755 --- a/integration/common/taskpool-sender/pom.xml +++ b/integration/common/taskpool-sender/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-taskpool-sender diff --git a/integration/common/variable-serializer/pom.xml b/integration/common/variable-serializer/pom.xml index 99aeab094..6c600419f 100755 --- a/integration/common/variable-serializer/pom.xml +++ b/integration/common/variable-serializer/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-variable-serializer diff --git a/pom.xml b/pom.xml index b42a5cff6..994a3f158 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-root - 3.0.2 + 3.0.3-SNAPSHOT pom POM: ${project.artifactId} diff --git a/view/form-url-resolver/pom.xml b/view/form-url-resolver/pom.xml index c707de2de..0c8b89d11 100644 --- a/view/form-url-resolver/pom.xml +++ b/view/form-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-form-url-resolver diff --git a/view/mongo/pom.xml b/view/mongo/pom.xml index c53ce6182..836586679 100755 --- a/view/mongo/pom.xml +++ b/view/mongo/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-view-mongo diff --git a/view/pom.xml b/view/pom.xml index 899a3e109..1c2059dab 100755 --- a/view/pom.xml +++ b/view/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.2 + 3.0.3-SNAPSHOT ../bom/parent/pom.xml diff --git a/view/simple/pom.xml b/view/simple/pom.xml index f112cf2f8..99ece3e2c 100755 --- a/view/simple/pom.xml +++ b/view/simple/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-view-simple diff --git a/view/view-api/pom.xml b/view/view-api/pom.xml index c4fb3ebcd..b964f96e0 100755 --- a/view/view-api/pom.xml +++ b/view/view-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.2 + 3.0.3-SNAPSHOT polyflow-view-api From d5797861e5d7126dcdfd5e5b9cca8478f887cf68 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Mon, 5 Jul 2021 17:13:00 +0200 Subject: [PATCH 02/27] docs: changes to introduction section --- docs/developer-guide/contribution.md | 1 - docs/developer-guide/project-setup.md | 34 +++++++++-------- .../scenarios/distributed-axon-server.md | 33 +++-------------- .../scenarios/distributed-no-axon-server.md | 37 ------------------- docs/examples/scenarios/single-node.md | 25 +------------ docs/introduction/index.md | 2 +- mkdocs.yml | 1 + 7 files changed, 26 insertions(+), 107 deletions(-) diff --git a/docs/developer-guide/contribution.md b/docs/developer-guide/contribution.md index 43e0f5c1a..9c7cff1bc 100644 --- a/docs/developer-guide/contribution.md +++ b/docs/developer-guide/contribution.md @@ -1,6 +1,5 @@ --- title: Contribution -pageId: 'contribution' --- There are several ways in which you may contribute to this project. diff --git a/docs/developer-guide/project-setup.md b/docs/developer-guide/project-setup.md index 29095b584..58ce0dd5c 100644 --- a/docs/developer-guide/project-setup.md +++ b/docs/developer-guide/project-setup.md @@ -47,8 +47,8 @@ automatically. ### Skip Frontend -TIP: Components for production use of camunda-bpm-taskpool are backend components only. Frontend components are only -created for examples and demonstration purpose. +!!! note + Components for production use of camunda-bpm-taskpool are backend components only. Frontend components are only created for examples and demonstration purpose. If you are interested in backend only, specify the `-DskipFrontend` switch. This will accelerate the build significantly. @@ -64,7 +64,8 @@ For doing so, you can re-generate the scripts running: ./mvnw -Pgenerate-sql ``` -NOTE: The existing scripts must not be replaced or changed, but new additional scripts needs to added. +!!! warning + The existing scripts must not be replaced or changed, but new additional scripts needs to added. ### Build Documentation @@ -83,10 +84,10 @@ For creation of documentation, please run: mkdocs build ``` -The docs are generated into `site` directory. +The docs are generated into `site` directory. -!!!note If you want to develop your docs in 'live' mode, run `mkdocs serve` and access -the [http://localhost:8000/](http://localhost:8000/) from your browser. +!!! note + If you want to develop your docs in 'live' mode, run `mkdocs serve` and access the [http://localhost:8000/](http://localhost:8000/) from your browser. ### Examples @@ -100,7 +101,8 @@ line or disable the `examples` module in your IDE. ## Local Start -!!!important If you want to run examples locally, you will need `docker` and `docker-compose`. +!!! important + If you want to run examples locally, you will need `docker` and `docker-compose`. ### Pre-requirements @@ -164,13 +166,10 @@ inside the corresponding `pom.xml`. Currently, all examples are _EXCLUDED_ from ### Trigger new release -WARNING: This operation requires special permissions. +!!! warning + This operation requires special permissions. -We use gitflow for development (see [A successful git branching model](http://nvie.com/posts/a-successful-git-branching-model/) -for more details). You could use gitflow with native git commands, but then you would have -to change the versions in the poms manually. Therefore, we use the -[mvn gitflow plugin](https://github.com/aleksandr-m/gitflow-maven-plugin/), which handles this and other -things nicely. +We use gitflow for development (see [A successful git branching model](http://nvie.com/posts/a-successful-git-branching-model/) for more details). You could use gitflow with native git commands, but then you would have to change the versions in the poms manually. Therefore, we use the [mvn gitflow plugin](https://github.com/aleksandr-m/gitflow-maven-plugin/), which handles this and other things nicely. You can build a release with: @@ -184,7 +183,8 @@ and update the `develop` branch for the new development version. ### Trigger a deploy -!!! warning This operation requires special permissions. +!!! warning + This operation requires special permissions. Currently, CI allows for deployment of artifacts to Maven Central and is executed using github actions. This means, that a push to `master` branch will start the corresponding build job, and if successful the @@ -192,7 +192,8 @@ artifacts will get into `Staging Repositories` of OSS Sonatype without manual in ### Run deploy from local machine -WARNING: This operation requires special permissions. +!!! warning + This operation requires special permissions. If you still want to execute the deployment from your local machine, you need to have GPG keys at place and to execute the following command on the `master` branch: @@ -205,7 +206,8 @@ export GPG_PASSPHRASE="" ### Release to public repositories -WARNING: This operation requires special permissions. +!!! warning + This operation requires special permissions. The deploy job will publish the artifacts to Nexus OSS staging repositories. Don't forget to close and release the repository to enable it's sync with Maven Central. diff --git a/docs/examples/scenarios/distributed-axon-server.md b/docs/examples/scenarios/distributed-axon-server.md index 034238142..26732df22 100644 --- a/docs/examples/scenarios/distributed-axon-server.md +++ b/docs/examples/scenarios/distributed-axon-server.md @@ -2,41 +2,18 @@ title: Distributed Scenario using Axon Server --- -## Scenario description - -A distributed scenario is helpful if you intend to build a central process platform and multiple process applications using it. - -In general, this is the main use case for taskpool itself, but the distribution aspects adds technical complexity to the resulting -architecture. Especially, following the architecture blueprint of Axon Framework, the three buses (command bus, event bus and -query bus) needs to be distributed and act as connecting infrastructure between components. - -Axon Server provides an implementation for this requirement leading to a distributed buses and a central event store. It is easy -to use, easy to configure and easy to run. If you need a HA setup, you will need the enterprise license of Axon Server. Essentially, -if don't have another HA ready-to use messaging, this scenario might be your way to go. - -This scenario supports: - -- central process platform components (including task pool and data pool) -- free choice for projection persistence (can be replayed) -- no direct communication between process platform and process application is required (e.g. via REST, since it is routed via command bus) +This example is demonstrating the usage of the Camunda BPM Taskpool with components distributed with help of Axon Server. +It provides two applications for demonstration purposes: the process application and the process platform. Both applications are built as SpringBoot applications. -The following configuration is used in the distributes scenario with Axon Server: +The following configuration is used in the distributed scenario with Axon Server: * Bus distribution is provided by Axon Server Connector (command bus, event bus, query bus) * Axon Server is used as Event Store * Postgresql is used as a database for: -- Camunda BPM Engine -- Process Application Datasource + - Camunda BPM Engine + - Process Application Datasource * Mongo is used as persistence for projection view (`mongo-view`) -The following diagram depicts the distribution of the components and the messaging: - -!["Deployment of taskpool with axon server"](../../img/deployment-axon-server.png) - -## Running Example - -This example is demonstrating the usage of the Camunda BPM Taskpool with components distributed with help of Axon Server. -It provides two applications for demonstration purposes: the process application and the process platform. Both applications are built as SpringBoot applications. ### System Requirements diff --git a/docs/examples/scenarios/distributed-no-axon-server.md b/docs/examples/scenarios/distributed-no-axon-server.md index 79b44d717..172cf6f3e 100644 --- a/docs/examples/scenarios/distributed-no-axon-server.md +++ b/docs/examples/scenarios/distributed-no-axon-server.md @@ -2,40 +2,3 @@ title: Scenario without Axon Server --- -## Scenario description - -If you already have another messaging at place, like Kafka or RabbitMQ, you might skip the usage of Axon Server. In doing so, -you will be responsible for distribution of events and will need to surrender some features. - -This scenario supports: - -- distributed task pool / data pool -- view must be persistent -- direct communication between task list / engines required (addressing, routing) -- concurrent access to engines might become a problem (no unit of work guarantees) - -The following diagram depicts the distribution of the components and the messaging. - -![Deployment of taskpool with other messaging](../../img/deployment-messaging.png) - -The following diagram depicts the task run from Process Application to the end user, consuming it via Tasklist API. - -![Kafka Message Run](../../img/scenario_kafka_messaging_overview.png) - -- The `CamundaEventingEnginePlugin` provided with the Taskpool tracks events in the Camunda engine (e.g. the creation, deletion or modification of a User Task) and makes them available as Spring events. -- The `Taskpool Collector` component listens to those events. It collects all relevant events that happen in a single transaction and registers a transaction synchronization to process them beforeCommit. Just before the transaction is committed, the collected events are accumulated and sent as Axon Commands through the `CommandGateway`. -- The `Taskpool Core` processes those commands and issues Axon Events through the EventGateway which are stored in Axon's database tables within the same transaction. -- The transaction commit finishes. If anything goes wrong before this point, the transaction rolls back and it is as though nothing ever happened. -- In the `Axon Kafka Extension`, a `TrackingEventProcessor` polls for events and sees them as soon as the transaction that created them is committed. It sends each event to Kafka and waits for an acknowledgment from Kafka. If sending fails or times out, the event processor goes into error mode and retries until it succeeds. This can lead to events being published to Kafka more than once but guarantees at-least-once delivery. -- Within the Tasklist API, the `Axon Kafka Extension` polls the events from Kafka and another TrackingEventProcessor forwards them to the `TaskPoolMongoService` where they are processed to update the Mongo DB accordingly. -- When a user queries the Tasklist API for tasks, two things happen: Firstly, the Mongo DB is queried for the current state of tasks for this user and these tasks are returned. Secondly, the Tasklist API subscribes to any changes to the Mongo DB. These changes are filtered for relevance to the user and relevant changes are returned after the current state as an infinite stream until the request is cancelled or interrupted for some reason. - -![Kafka Message Transaction Overview](../../img/scenario_kafka_messaging_tx_view.png) - -### From Process Application to Kafka - -![Process Application to Kafka Messaging](../../img/scenario_process_application_to_kafka_detail.png) - -### From Kafka to Tasklist API - -![Kafka to Tasklist API Messaging](../../img/scenario_kafka_to_tasklist_detail.png) diff --git a/docs/examples/scenarios/single-node.md b/docs/examples/scenarios/single-node.md index bb6d9f8d2..0c725ea00 100644 --- a/docs/examples/scenarios/single-node.md +++ b/docs/examples/scenarios/single-node.md @@ -1,31 +1,8 @@ --- -pageId: single-node title: Scenario for running on a single node --- -## Scenario description - -In a single node scenario, the process application and the process platform components are deployed in a single node. -In most production environments this scenario doesn't make sense because of poor reliability. Still, it is valid for -demonstration purpose and is ideal to play around with components and understand their purpose and interaction between them. - -In a single node scenario the following configuration is used: - -* All buses are local (command bus, event bus, query bus) -* In-memory H2 is used as a database for: - - Camunda BPM Engine - - Axon Event Store (JPA-based) - - Process Application Datasource -* In-memory transient projection view is used (`simple-view`) - -Check the following diagram for more details: - -![Deployment of all component in a single node](../../img/deployment-single.png) - -## Running Example - -This example demonstrates the usage of the Camunda BPM Taskpool deployed in one single node and is -built as a SpringBoot application. +This example demonstrates the usage of the Camunda BPM Taskpool deployed in one single node and is built as a SpringBoot application described in the [Deployment](../../indroduction/deployment.md) section. ### System Requirements diff --git a/docs/introduction/index.md b/docs/introduction/index.md index 5816bc20d..6be52cf92 100644 --- a/docs/introduction/index.md +++ b/docs/introduction/index.md @@ -1,5 +1,5 @@ --- -title: Motivation +title: Motivation and Goals --- Over the last years, we built various process applications and whole process platforms for our customers using a modern process engine - Camunda BPM. In doing so, we were observed common requirements, in particular with respect to task-oriented frontend applications and were able to extract them. These were basic requirements independent of the used frontend technology and it turned out that some issues occurred every time during the implementation. diff --git a/mkdocs.yml b/mkdocs.yml index 2650cea1a..fdb3d8cf2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -98,6 +98,7 @@ nav: - Concepts: introduction/concepts.md - Features: introduction/features.md - Solution Architecture: introduction/solution-architecture.md + - Deployment Strategies: introduction/deployment.md - Getting Started: getting-started/index.md - Reference: - Reference Overview: reference-guide/index.md From c2543189592ebcdeca523cdd23834eb267ba2c6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:01:06 +0000 Subject: [PATCH 03/27] chore(deps): bump dokka-maven-plugin from 1.4.32 to 1.5.0 Bumps [dokka-maven-plugin](https://github.com/Kotlin/dokka) from 1.4.32 to 1.5.0. - [Release notes](https://github.com/Kotlin/dokka/releases) - [Commits](https://github.com/Kotlin/dokka/compare/v1.4.32...v1.5.0) --- updated-dependencies: - dependency-name: org.jetbrains.dokka:dokka-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- bom/parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index f5481859c..b7df5a3c0 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -598,7 +598,7 @@ org.jetbrains.dokka dokka-maven-plugin - 1.4.32 + 1.5.0 attach-javadocs From 04177db05a46442d634b645e4041138f50f96222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20G=C3=BCnther?= Date: Thu, 8 Jul 2021 14:08:15 +0200 Subject: [PATCH 04/27] docs: fix name of starter in getting started docs --- docs/getting-started/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index cddde069e..9a91981e6 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -20,7 +20,7 @@ to your `pom.xml`: ``` xml io.holunda.polyflow - polyflow-integration-camunda-bpm-engine-starter + polyflow-camunda-bpm-springboot-starter ${polyflow.version} ``` From c807cab0df8977d0ddf7833dce0590775c7127cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jul 2021 16:00:49 +0000 Subject: [PATCH 05/27] chore(deps): bump version.camunda.spin from 1.10.1 to 1.11.0 Bumps `version.camunda.spin` from 1.10.1 to 1.11.0. Updates `camunda-spin-dataformat-json-jackson` from 1.10.1 to 1.11.0 - [Release notes](https://github.com/camunda/camunda-spin/releases) - [Commits](https://github.com/camunda/camunda-spin/compare/1.10.1...1.11.0) Updates `camunda-spin-core` from 1.10.1 to 1.11.0 - [Release notes](https://github.com/camunda/camunda-spin/releases) - [Commits](https://github.com/camunda/camunda-spin/compare/1.10.1...1.11.0) --- updated-dependencies: - dependency-name: org.camunda.spin:camunda-spin-dataformat-json-jackson dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.camunda.spin:camunda-spin-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index bb41c897b..6017764df 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -38,7 +38,7 @@ 8.0.0 4.13.0 1.2.3 - 1.10.1 + 1.11.0 2.4.5 From 630594ad1c61897c6061bfcacf6d89888ce64755 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Jul 2021 16:00:48 +0000 Subject: [PATCH 06/27] chore(deps): bump openapi-generator-maven-plugin from 5.1.1 to 5.2.0 Bumps openapi-generator-maven-plugin from 5.1.1 to 5.2.0. --- updated-dependencies: - dependency-name: org.openapitools:openapi-generator-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index bb41c897b..03958d2f4 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -188,7 +188,7 @@ org.openapitools openapi-generator-maven-plugin - 5.1.1 + 5.2.0 From f8a8d10122674cf96bba6a9d19cf0756933f372e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:01:51 +0000 Subject: [PATCH 07/27] chore(deps): bump kotlin.version from 1.5.20 to 1.5.21 Bumps `kotlin.version` from 1.5.20 to 1.5.21. Updates `kotlin-bom` from 1.5.20 to 1.5.21 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.21/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.20...v1.5.21) Updates `kotlin-test-junit` from 1.5.20 to 1.5.21 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.5.21/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.5.20...v1.5.21) Updates `kotlin-maven-allopen` from 1.5.20 to 1.5.21 Updates `kotlin-maven-noarg` from 1.5.20 to 1.5.21 Updates `kotlin-maven-plugin` from 1.5.20 to 1.5.21 --- updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlin:kotlin-test-junit dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlin:kotlin-maven-allopen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlin:kotlin-maven-noarg dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlin:kotlin-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 994a3f158..f1b969e91 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ ${java.version} ${java.version} - 1.5.20 + 1.5.21 ${java.version} true 2.0.8 From 5c54f493c7b9a590de4208de991c3ea544564cc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jul 2021 16:01:17 +0000 Subject: [PATCH 08/27] chore(deps): bump kotlin-logging-jvm from 2.0.8 to 2.0.10 Bumps [kotlin-logging-jvm](https://github.com/MicroUtils/kotlin-logging) from 2.0.8 to 2.0.10. - [Release notes](https://github.com/MicroUtils/kotlin-logging/releases) - [Changelog](https://github.com/MicroUtils/kotlin-logging/blob/master/ChangeLog.md) - [Commits](https://github.com/MicroUtils/kotlin-logging/compare/2.0.8...2.0.10) --- updated-dependencies: - dependency-name: io.github.microutils:kotlin-logging-jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f1b969e91..51ab42411 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 1.5.21 ${java.version} true - 2.0.8 + 2.0.10 From 71dbdcdf2163930221659565a83abfa59ed13924 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Jul 2021 16:01:09 +0000 Subject: [PATCH 09/27] chore(deps): bump axon.version from 4.5.2 to 4.5.3 Bumps `axon.version` from 4.5.2 to 4.5.3. Updates `axon-messaging` from 4.5.2 to 4.5.3 - [Release notes](https://github.com/AxonFramework/AxonFramework/releases) - [Changelog](https://github.com/AxonFramework/AxonFramework/blob/master/axon-4-api-changes.md) - [Commits](https://github.com/AxonFramework/AxonFramework/compare/axon-4.5.2...axon-4.5.3) Updates `axon-eventsourcing` from 4.5.2 to 4.5.3 - [Release notes](https://github.com/AxonFramework/AxonFramework/releases) - [Changelog](https://github.com/AxonFramework/AxonFramework/blob/master/axon-4-api-changes.md) - [Commits](https://github.com/AxonFramework/AxonFramework/compare/axon-4.5.2...axon-4.5.3) Updates `axon-modelling` from 4.5.2 to 4.5.3 - [Release notes](https://github.com/AxonFramework/AxonFramework/releases) - [Changelog](https://github.com/AxonFramework/AxonFramework/blob/master/axon-4-api-changes.md) - [Commits](https://github.com/AxonFramework/AxonFramework/compare/axon-4.5.2...axon-4.5.3) Updates `axon-configuration` from 4.5.2 to 4.5.3 Updates `axon-spring-boot-starter` from 4.5.2 to 4.5.3 - [Release notes](https://github.com/AxonFramework/AxonFramework/releases) - [Changelog](https://github.com/AxonFramework/AxonFramework/blob/master/axon-4-api-changes.md) - [Commits](https://github.com/AxonFramework/AxonFramework/compare/axon-4.5.2...axon-4.5.3) Updates `axon-spring` from 4.5.2 to 4.5.3 - [Release notes](https://github.com/AxonFramework/AxonFramework/releases) - [Changelog](https://github.com/AxonFramework/AxonFramework/blob/master/axon-4-api-changes.md) - [Commits](https://github.com/AxonFramework/AxonFramework/compare/axon-4.5.2...axon-4.5.3) Updates `axon-test` from 4.5.2 to 4.5.3 - [Release notes](https://github.com/AxonFramework/AxonFramework/releases) - [Changelog](https://github.com/AxonFramework/AxonFramework/blob/master/axon-4-api-changes.md) - [Commits](https://github.com/AxonFramework/AxonFramework/compare/axon-4.5.2...axon-4.5.3) --- updated-dependencies: - dependency-name: org.axonframework:axon-messaging dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.axonframework:axon-eventsourcing dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.axonframework:axon-modelling dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.axonframework:axon-configuration dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.axonframework:axon-spring-boot-starter dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: org.axonframework:axon-spring dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.axonframework:axon-test dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- bom/parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index b7df5a3c0..abc458736 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -21,7 +21,7 @@ 2.4.5 7.15.0 - 4.5.2 + 4.5.3 0.1.0 0.0.4 From d32f7a82dc04788b6216bcf67a226ffdc2cefdda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jul 2021 16:01:57 +0000 Subject: [PATCH 10/27] chore(deps): bump maven-enforcer-plugin from 3.0.0-M3 to 3.0.0 Bumps [maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.0.0-M3 to 3.0.0. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.0.0-M3...enforcer-3.0.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f1b969e91..b0d9f69da 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.0.0 enforce-maven From cb3d6d779ef8c7b1c86ffe9b6c84add92e9d23d7 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Mon, 2 Aug 2021 11:25:15 +0200 Subject: [PATCH 11/27] docs: add deployment --- docs/introduction/deployment.md | 85 +++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 docs/introduction/deployment.md diff --git a/docs/introduction/deployment.md b/docs/introduction/deployment.md new file mode 100644 index 000000000..c58acdf62 --- /dev/null +++ b/docs/introduction/deployment.md @@ -0,0 +1,85 @@ +--- +title: Deployment Scenarios +--- +Depending on your requirements and infrastructure available several deployment scenarios of the components are possible. + +The simplest setup is to run all components on a single node. A more advanced scenario is to distribute components over the network and connect them. + +In doing so, one of the challenging issues for distribution and connecting microservices is a setup of messaging technology +supporting required message exchange patterns (MEPs) for a CQRS system. Because of different semantics of commands, +events and queries and additional requirements of event-sourced persistence a special implementation of +command bus, event bus and event store is required. In particular, two scenarios can be distinguished: using Axon Server +or using a different distribution technology. + +## Single node deployment + +The easiest scenario is the **Single Node Deployment**. It provides all functional features of the Polyflow library, but is not not addressing any of performance, scalability, autonomy and reliability requirements. It works almost without additional infrastructure and is ideal to start with. + +In a single node scenario the following configuration is used: + +* All buses are local (command bus, event bus, query bus) +* Camunda BPM Integration components, Core components and View components are all deployed in the same node +* JPA-Based event storage is used, persisting the domain events in a RDBMS, along with Camunda-specific DB tables. +* Simple (In-memory) View is used to provide projections of `taskpool` and `datapool` + +Check the following diagram for more details: + +![Deployment of all component in a single node](../img/deployment-single.png) + +## Multiple node deployment + +The more advanced scenario is to separate the **Process Platform components** from **Process Application components**. + +It is helpful if you intend to build a central **Process Platform** and multiple **Process applications** using it. + +In general, this is the main use case for Polyflow framework itself, but the distribution aspects adds technical complexity to the resulting architecture. Especially, following the architecture blueprint of Axon Framework, the three buses (command bus, event bus and query bus) needs to be distributed and act as connecting infrastructure between components. + +### Distribution using Axon Server + +Axon Server provides an implementation for this requirement leading to a distributed buses and a central Event Store. It is easy to use, easy to configure and easy to run. If you need a HA setup, you will need the enterprise license of Axon Server. Essentially, if don't have another HA ready-to use messaging, this scenario might be your way to go. + +This scenario supports: + +- central Process Platform components (including task pool and data pool) +- free choice for projection persistence (since Axon Server supports) +- no direct synchronous communication between **Process Platform** and **Process Application** is required (e.g. via REST, since it is routed via command bus) + +The following diagram depicts the distribution of the components and the messaging: + +![Deployment of Polyflow with Axon server](../img/deployment-axon-server.png) + +### Distribution without Axon Server + +If you already have another messaging at place, like Kafka or RabbitMQ, you might skip the usage of Axon Server. In doing so, you will be responsible for distribution of events and will need to surrender some features. + +This scenario supports: + +- distributed task pool / data pool +- view **MUST** be persistent (no replay supported) +- direct communication between task list / engines required (addressing, routing) +- concurrent access to engines might become a problem (no unit of work guarantees) + +The following diagram depicts the distribution of the components and the messaging. + +![Deployment of taskpool with other messaging](../img/deployment-messaging.png) + +The following diagram depicts the task run from Process Application to the end user, consuming it via Tasklist API. + +![Kafka Message Run](../img/scenario_kafka_messaging_overview.png) + +- The `Camunda BPM Taskpool Collector` component listens to Camunda events, collects all relevant events that happen in a single transaction and registers a transaction synchronization to process them beforeCommit. Just before the transaction is committed, the collected events are accumulated and sent as Axon Commands through the `CommandGateway`. +- The `Taskpool Core` processes those commands and issues Axon Events through the EventGateway which are stored in Axon's database tables within the same transaction. +- The transaction commit finishes. If anything goes wrong before this point, the transaction rolls back and it is as though nothing ever happened. +- In the `Axon Kafka Extension`, a `TrackingEventProcessor` polls for events and sees them as soon as the transaction that created them is committed. It sends each event to Kafka and waits for an acknowledgment from Kafka. If sending fails or times out, the event processor goes into error mode and retries until it succeeds. This can lead to events being published to Kafka more than once but guarantees at-least-once delivery. +- Within the Tasklist API, the `Axon Kafka Extension` polls the events from Kafka and another TrackingEventProcessor forwards them to the `TaskPoolMongoService` where they are processed to update the Mongo DB accordingly. +- When a user queries the Tasklist API for tasks, two things happen: Firstly, the Mongo DB is queried for the current state of tasks for this user and these tasks are returned. Secondly, the Tasklist API subscribes to any changes to the Mongo DB. These changes are filtered for relevance to the user and relevant changes are returned after the current state as an infinite stream until the request is cancelled or interrupted for some reason. + +![Kafka Message Transaction Overview](../img/scenario_kafka_messaging_tx_view.png) + +#### From Process Application to Kafka + +![Process Application to Kafka Messaging](../img/scenario_process_application_to_kafka_detail.png) + +#### From Kafka to Tasklist API + +![Kafka to Tasklist API Messaging](../img/scenario_kafka_to_tasklist_detail.png) From 24e5b50e094d008860927bc234b4a645912739de Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Mon, 2 Aug 2021 22:20:55 +0200 Subject: [PATCH 12/27] feat: implement jpa datapool view --- bom/parent/pom.xml | 3 + view/jpa/pom.xml | 145 +++++++++++++++ .../view/jpa/EnablePolyflowJpaView.kt | 10 ++ .../view/jpa/PolyflowJpaViewConfiguration.kt | 8 + .../view/jpa/auth/AuthorizationPrincipal.kt | 60 +++++++ .../auth/AuthorizationPrincipalRepository.kt | 5 + .../polyflow/view/jpa/data/Converters.kt | 169 ++++++++++++++++++ .../polyflow/view/jpa/data/DataEntryEntity.kt | 89 +++++++++ .../view/jpa/data/DataEntryRepository.kt | 23 +++ .../view/jpa/data/DataEntryStateEmbeddable.kt | 19 ++ .../polyflow/view/jpa/data/ProtocolElement.kt | 30 ++++ .../main/resources/META-INF/persistence.xml | 7 + .../view/jpa/JpaPolyflowViewService.kt | 164 +++++++++++++++++ .../polyflow/view/jpa/TestApplication.kt | 6 + .../jpa/auth/AuthorizationPrincipalIdTest.kt | 27 +++ .../polyflow/view/jpa/data/DataEntryIdTest.kt | 22 +++ .../view/jpa/data/DataEntryRepositoryITest.kt | 159 ++++++++++++++++ .../src/test/resources/application-itest.yaml | 12 ++ view/jpa/src/test/resources/banner.txt | 3 + view/jpa/src/test/resources/logback.xml | 11 ++ .../polyflow/view/mongo/MongoViewService.kt | 2 +- view/pom.xml | 1 + 22 files changed, 974 insertions(+), 1 deletion(-) create mode 100755 view/jpa/pom.xml create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt create mode 100644 view/jpa/src/main/resources/META-INF/persistence.xml create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt create mode 100644 view/jpa/src/test/resources/application-itest.yaml create mode 100644 view/jpa/src/test/resources/banner.txt create mode 100644 view/jpa/src/test/resources/logback.xml diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index abc458736..6f11f88c6 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -471,6 +471,9 @@ org.jetbrains.kotlin ${kotlin.version} + + -Xjsr305=strict + spring jpa diff --git a/view/jpa/pom.xml b/view/jpa/pom.xml new file mode 100755 index 000000000..ed33308b4 --- /dev/null +++ b/view/jpa/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + + io.holunda.polyflow + polyflow-view-parent + 3.0.3-SNAPSHOT + + + polyflow-view-jpa + core/view/${project.artifactId} + + + + io.holunda.polyflow + polyflow-view-api + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.axonframework + axon-messaging + + + org.axonframework + axon-configuration + + + io.projectreactor + reactor-core + + + io.projectreactor.kotlin + reactor-kotlin-extensions + + + + + org.springframework.boot + spring-boot-starter-test + test + + + com.h2database + h2 + test + + + com.tngtech.jgiven + jgiven-junit + test + + + com.tngtech.jgiven + jgiven-spring + test + + + com.tngtech.jgiven + jgiven-spring-junit4 + test + + + com.tngtech.jgiven + jgiven-html5-report + test + + + org.mockito.kotlin + mockito-kotlin + test + + + org.awaitility + awaitility + test + + + io.projectreactor + reactor-test + test + + + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + + + + + + + + + + + + + + generate-sql + + + + de.juplo + hibernate-maven-plugin + 2.1.1 + + + generate-h2-ddl + + create + + + org.hibernate.dialect.H2Dialect + + org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + + false + true + true + h2_ddl.sql + + + + + + javax.xml.bind + jaxb-api + 2.4.0-b180830.0359 + + + + + + + + diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt new file mode 100644 index 000000000..39c102d86 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt @@ -0,0 +1,10 @@ +package io.holunda.polyflow.view.jpa + +import org.springframework.context.annotation.Import + +/** + * Enables polyflow projection using RDMBS via JPA as persistence. + */ +@MustBeDocumented +@Import(io.holunda.polyflow.view.jpa.PolyflowJpaViewConfiguration::class) +annotation class EnablePolyflowJpaView diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt new file mode 100644 index 000000000..33a1d7972 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt @@ -0,0 +1,8 @@ +package io.holunda.polyflow.view.jpa + +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.context.annotation.Configuration + +@Configuration +@EntityScan +class PolyflowJpaViewConfiguration diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt new file mode 100644 index 000000000..a2223511e --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt @@ -0,0 +1,60 @@ +package io.holunda.polyflow.view.jpa.auth + +import java.io.Serializable +import java.util.* +import javax.persistence.* + +/** + * Authorization principal (user or group). + */ +@Entity(name = "AUTHORIZATION_PRINCIPAL") +class AuthorizationPrincipal( + @EmbeddedId + var id: AuthorizationPrincipalId +) { + companion object { + fun group(name: String) = AuthorizationPrincipal(AuthorizationPrincipalId.invoke("${AuthorizationPrincipalType.GROUP.name}:$name")) + fun user(name: String) = AuthorizationPrincipal(AuthorizationPrincipalId.invoke("${AuthorizationPrincipalType.USER.name}:$name")) + } +} + +/** + * Composite to use for authorization. + */ +@Embeddable +class AuthorizationPrincipalId( + @Column(name = "AUTH_NAME", nullable = false) + var name: String, + @Column(name = "AUTH_TYPE", nullable = false) + @Enumerated(EnumType.STRING) + var type: AuthorizationPrincipalType +) : Serializable { + companion object { + operator fun invoke(auth: String) = auth.split(":").let { + require(it.size == 2) { "Illegal auth format, expecting :" } + AuthorizationPrincipalId(type = AuthorizationPrincipalType.valueOf(it[0]), name = it[1]) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is AuthorizationPrincipalId) return false + return Objects.equals(this.name, other.name) && + Objects.equals(this.type, other.type) + } + + override fun hashCode(): Int { + return Objects.hash(this.name, this.type) + } + + override fun toString(): String = "$type:$name" + +} + +/** + * Authorization type. + */ +enum class AuthorizationPrincipalType { + GROUP, + USER +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt new file mode 100644 index 000000000..2a640dd72 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt @@ -0,0 +1,5 @@ +package io.holunda.polyflow.view.jpa.auth + +import org.springframework.data.repository.CrudRepository + +interface AuthorizationPrincipalRepository : CrudRepository diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt new file mode 100644 index 000000000..d6bc971f7 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt @@ -0,0 +1,169 @@ +package io.holunda.polyflow.view.jpa.data + +import com.fasterxml.jackson.databind.ObjectMapper +import io.holixon.axon.gateway.query.RevisionValue +import io.holunda.camunda.taskpool.api.business.* +import io.holunda.polyflow.view.DataEntry +import io.holunda.polyflow.view.ProtocolEntry +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipalType +import org.camunda.bpm.engine.variable.VariableMap +import org.camunda.bpm.engine.variable.Variables.createVariables + +/** + * Converts the entity into API type. + */ +fun DataEntryEntity.toDataEntry(objectMapper: ObjectMapper) = + DataEntry( + entryType = this.dataEntryId.entryType, + entryId = this.dataEntryId.entryId, + type = this.type, + name = this.name, + description = this.description, + applicationName = this.applicationName, + formKey = this.formKey, + state = this.state.toState(), + protocol = this.protocol.map { it.toProtocolEntry() }, + authorizedUsers = this.authorizedPrincipals.asUsernames(), + authorizedGroups = this.authorizedPrincipals.asGroupnames(), + payload = this.payload.toPayloadVariableMap(objectMapper) + ) + +/** + * Retrieves user authorizations as list of user names. + */ +fun Set.asUsernames() = this.filter { it.id.type == AuthorizationPrincipalType.USER }.map { it.id.name }.toSet() + +/** + * Retrieves group authorizations as list of group names. + */ +fun Set.asGroupnames() = this.filter { it.id.type == AuthorizationPrincipalType.GROUP }.map { it.id.name }.toSet() + +/** + * Serializes payload as JSON. + */ +fun VariableMap.toPayloadJson(objectMapper: ObjectMapper): String = + objectMapper.writeValueAsString(this) + +/** + * Deserializes JSON back into variable map. + */ +fun String?.toPayloadVariableMap(objectMapper: ObjectMapper): VariableMap = if (this != null) { + val mapType = objectMapper.typeFactory.constructMapType(Map::class.java, String::class.java, Any::class.java) + val map: Map = objectMapper.convertValue(this, mapType) + createVariables().apply { + putAll(map) + } +} else { + createVariables() +} + +/** + * Converts entity to API type. + */ +fun ProtocolElement.toProtocolEntry() = + ProtocolEntry( + time = this.time, + username = this.username, + state = this.state.toState(), + logMessage = this.logMessage, + logDetails = this.logDetails + ) + +/** + * Converts stored value into API type. + */ +fun DataEntryStateEmbeddable.toState(): DataEntryState = ProcessingType.valueOf(this.processingType).of(this.state) + +/** + * Converts API state into persistence format. + */ +fun DataEntryState.toState() = DataEntryStateEmbeddable(processingType = this.processingType.name, state = this.state ?: "") + +/** + * Event to entity. + */ +fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue) = DataEntryEntity( + dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), + payload = this.payload.toPayloadJson(objectMapper), + name = this.name, + applicationName = this.applicationName, + type = this.type, + description = this.description, + state = this.state.toState(), + formKey = this.formKey, + revision = if (revisionValue != RevisionValue.NO_REVISION) { + revisionValue.revision + } else { + 0L + }, + authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.user(it) } + .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.group(it) }).toSet(), +).apply { + this.protocol = this.protocol.addModification(this, this@toEntity.createModification, this@toEntity.state) +} + +/** + * Event to entity for an update, if an optional entry exists. + */ +fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue, oldEntry: DataEntryEntity?) = if (oldEntry == null) { + DataEntryEntity( + dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), + payload = this.payload.toPayloadJson(objectMapper), + name = this.name, + applicationName = this.applicationName, + type = this.type, + description = this.description, + state = this.state.toState(), + formKey = this.formKey, + authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.user(it) } + .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.group(it) }).toSet(), + revision = if (revisionValue != RevisionValue.NO_REVISION) { + revisionValue.revision + } else { + 0L + }, + ) +} else { + oldEntry.also { + it.payload = this.payload.toPayloadJson(objectMapper) + it.name = this.name + it.applicationName = this.applicationName + it.type = this.type + it.description = this.description + it.state = this.state.toState() + it.formKey = this.formKey + it.authorizedPrincipals = + AuthorizationChange.applyUserAuthorization( + it.authorizedPrincipals.asUsernames(), + this.authorizations + ).map { AuthorizationPrincipal.user(it) } + .plus(AuthorizationChange.applyGroupAuthorization( + it.authorizedPrincipals.asGroupnames(), + this.authorizations + ).map { AuthorizationPrincipal.group(it) }) + .toSet() + it.revision = if (revisionValue != RevisionValue.NO_REVISION) { + revisionValue.revision + } else { + it.revision + } + } +}.apply { + this.protocol = this.protocol.addModification(this, this@toEntity.updateModification, this@toEntity.state) +} + +/** + * Adds a modification to the protocol. + */ +fun List.addModification(dataEntry: DataEntryEntity, modification: Modification, state: DataEntryState) = + this.plus( + ProtocolElement( + dataEntry = dataEntry, + time = modification.time.toInstant(), + username = modification.username, + logMessage = modification.log, + logDetails = modification.logNotes, + state = state.toState() + ) + ) diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt new file mode 100644 index 000000000..33bb09f5c --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt @@ -0,0 +1,89 @@ +package io.holunda.polyflow.view.jpa.data + + +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal +import java.io.Serializable +import java.time.Instant +import java.util.* +import javax.persistence.* + +@Entity(name = "DATA_ENTRY") +class DataEntryEntity( + @EmbeddedId + var dataEntryId: DataEntryId, + @Column(name = "TYPE", nullable = false) + var type: String, + @Column(name = "NAME", nullable = false) + var name: String, + @Column(name = "APPLICATION_NAME", nullable = false) + var applicationName: String, + @Column(name = "FORM_KEY") + var formKey: String? = null, + @Column(name = "REVISION") + var revision: Long = 0L, + @Embedded + var state: DataEntryStateEmbeddable, + + @Column(name = "DESCRIPTION") + var description: String? = null, + + @Column(name = "DATE_CREATED", nullable = false) + var createdDate: Instant = Instant.now(), + + @Column(name = "DATE_LAST_MODIFIED", nullable = false) + var lastModifiedDate: Instant = Instant.now(), + + @ManyToMany(fetch = FetchType.EAGER, targetEntity = AuthorizationPrincipal::class) + @JoinTable( + name = "DATA_ENTRY_AUTHORIZATIONS", + inverseJoinColumns = [ + JoinColumn(name = "AUTH_NAME", referencedColumnName = "AUTH_NAME"), + JoinColumn(name = "AUTH_TYPE", referencedColumnName = "AUTH_TYPE"), + ], + joinColumns = [ + JoinColumn(name = "ENTRY_TYPE", referencedColumnName = "ENTRY_TYPE"), + JoinColumn(name = "ENTRY_ID", referencedColumnName = "ENTRY_ID"), + ], + + ) + var authorizedPrincipals: Set = setOf(), + + @OneToMany(mappedBy = "dataEntry", orphanRemoval = true, cascade = [CascadeType.ALL], targetEntity = ProtocolElement::class) + var protocol: List = mutableListOf(), + + @Lob + var payload: String? = null, +) { + override fun toString(): String { + return "DataEntry[entryType = ${dataEntryId.entryType}, entryId = ${dataEntryId.entryId}, name = $name]" + } +} + +@Embeddable +class DataEntryId( + @Column(name = "ENTRY_ID", nullable = false) + var entryId: String, + @Column(name = "ENTRY_TYPE", nullable = false) + var entryType: String +) : Serializable { + + companion object { + operator fun invoke(identity: String): DataEntryId = identity.split(":").let { + require(it.size == 2) { "Illegal identity format, expecting :" } + DataEntryId(entryType = it[0], entryId = it[1]) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DataEntryId) return false + return Objects.equals(this.entryId, other.entryId) && + Objects.equals(this.entryType, other.entryType) + } + + override fun hashCode(): Int { + return Objects.hash(this.entryId, this.entryType) + } + + override fun toString(): String = "$entryType:$entryId" +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt new file mode 100644 index 000000000..ea25a0d03 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt @@ -0,0 +1,23 @@ +package io.holunda.polyflow.view.jpa.data + +import io.holunda.camunda.taskpool.api.business.ProcessingType +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal +import org.springframework.data.jpa.domain.Specification +import org.springframework.data.jpa.repository.JpaSpecificationExecutor +import org.springframework.data.repository.CrudRepository + +interface DataEntryRepository : CrudRepository, JpaSpecificationExecutor { + + fun findAllByAuthorizedPrincipalsIn(authorizedPrincipals: Set): List + + companion object { + fun hasState(state: String): Specification = + Specification { dataEntry, _, builder -> builder.equal(dataEntry.get("state").get("state"), state) } + + fun hasProcessingType(processingType: ProcessingType): Specification = + Specification { dataEntry, _, builder -> builder.equal(dataEntry.get("state").get("processingType"), processingType) } + + } +} + + diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt new file mode 100644 index 000000000..c402dd049 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt @@ -0,0 +1,19 @@ +package io.holunda.polyflow.view.jpa.data + +import io.holunda.camunda.taskpool.api.business.DataEntryState +import java.io.Serializable +import javax.persistence.Column +import javax.persistence.Embeddable + +@Embeddable +class DataEntryStateEmbeddable( + @Column(name = "PROCESSING_TYPE", nullable = false) + val processingType: String, + @Column(name = "STATE", nullable = false) + val state: String +) : Serializable { + companion object { + operator fun invoke(state: DataEntryState): DataEntryStateEmbeddable = + DataEntryStateEmbeddable(processingType = state.processingType.name, state = state.state ?: "") + } +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt new file mode 100644 index 000000000..0d1ec130e --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt @@ -0,0 +1,30 @@ +package io.holunda.polyflow.view.jpa.data + +import java.time.Instant +import java.util.* +import javax.persistence.* + +@Entity(name = "PROTOCOL") +class ProtocolElement( + @Id + @Column(name = "ID") + var id: String = UUID.randomUUID().toString(), + + @Column(name = "TIME", nullable = false) + val time: Instant = Instant.now(), + @Embedded + val state: DataEntryStateEmbeddable, + @Column(name = "USERNAME", nullable = true) + val username: String? = null, + @Column(name = "LOG_MESSAGE", nullable = true) + val logMessage: String? = null, + @Column(name = "LOG_DETAILS", nullable = true) + val logDetails: String? = null, + + @ManyToOne + @JoinColumns( + JoinColumn(name = "ENTRY_TYPE", referencedColumnName = "ENTRY_TYPE", nullable = false), + JoinColumn(name = "ENTRY_ID", referencedColumnName = "ENTRY_ID", nullable = false) + ) + var dataEntry: DataEntryEntity +) diff --git a/view/jpa/src/main/resources/META-INF/persistence.xml b/view/jpa/src/main/resources/META-INF/persistence.xml new file mode 100644 index 000000000..c7bfa4126 --- /dev/null +++ b/view/jpa/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,7 @@ + + + io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal + io.holunda.polyflow.view.jpa.data.DataEntryEntity + io.holunda.polyflow.view.jpa.data.ProtocolElement + + diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt new file mode 100644 index 000000000..97ddebf20 --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt @@ -0,0 +1,164 @@ +package io.holunda.polyflow.view.jpa + +import com.fasterxml.jackson.databind.ObjectMapper +import io.holixon.axon.gateway.query.QueryResponseMessageResponseType +import io.holixon.axon.gateway.query.RevisionValue +import io.holunda.camunda.taskpool.api.business.DataEntryCreatedEvent +import io.holunda.camunda.taskpool.api.business.DataEntryUpdatedEvent +import io.holunda.polyflow.view.jpa.JpaPolyflowViewService.Companion.PROCESSING_GROUP +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipalRepository +import io.holunda.polyflow.view.jpa.data.DataEntryEntity +import io.holunda.polyflow.view.jpa.data.DataEntryId +import io.holunda.polyflow.view.jpa.data.DataEntryRepository +import io.holunda.polyflow.view.jpa.data.toEntity +import io.holunda.polyflow.view.jpa.data.toDataEntry +import io.holunda.polyflow.view.query.data.* +import mu.KLogging +import org.axonframework.config.ProcessingGroup +import org.axonframework.eventhandling.EventHandler +import org.axonframework.messaging.MetaData +import org.axonframework.queryhandling.QueryHandler +import org.axonframework.queryhandling.QueryResponseMessage +import org.axonframework.queryhandling.QueryUpdateEmitter +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Component + +@Component +@ProcessingGroup(PROCESSING_GROUP) +class JpaPolyflowViewService( + val dataEntryRepository: DataEntryRepository, + val authorizationPrincipalRepository: AuthorizationPrincipalRepository, + val objectMapper: ObjectMapper, + val queryUpdateEmitter: QueryUpdateEmitter +) : DataEntryApi { + + companion object : KLogging() { + const val PROCESSING_GROUP = "io.holunda.polyflow.view.jpa.service" + } + + @QueryHandler + override fun query(query: DataEntryForIdentityQuery, metaData: MetaData): QueryResponseMessage { + val entryId = query.entryId + require(entryId != null) { "Entry id must be set on query by id" } + + val elements = dataEntryRepository.findAllById(listOf(DataEntryId(entryId = entryId, entryType = query.entryType))) + val payload = DataEntriesQueryResult(elements = elements.map { it.toDataEntry(objectMapper) }).slice(query = query) + + return QueryResponseMessageResponseType.asQueryResponseMessage( + payload = payload, + metaData = getMaxRevision(elements.filter { dataEntryEntity -> + payload.elements.map { dataEntry -> dataEntry.entryType to dataEntry.entryId } + .contains(dataEntryEntity.dataEntryId.entryType to dataEntryEntity.dataEntryId.entryId) + }.map { it.revision }).toMetaData() + ) + } + + @QueryHandler + override fun query(query: DataEntriesForUserQuery, metaData: MetaData): QueryResponseMessage { + + // FIXME, construct query based on the filters. + + val authorizationQuery = setOf( + AuthorizationPrincipal.user(query.user.username), + ).plus(query.user.groups.map { AuthorizationPrincipal.group(it) }) + + val elements = dataEntryRepository.findAllByAuthorizedPrincipalsIn(authorizationQuery) + val payload = DataEntriesQueryResult(elements = elements.map { it.toDataEntry(objectMapper) }).slice(query = query) + + return QueryResponseMessageResponseType.asQueryResponseMessage( + payload = payload, + metaData = getMaxRevision(elements.filter { dataEntryEntity -> + payload.elements.map { dataEntry -> dataEntry.entryType to dataEntry.entryId } + .contains(dataEntryEntity.dataEntryId.entryType to dataEntryEntity.dataEntryId.entryId) + }.map { it.revision }).toMetaData() + ) + + } + + @QueryHandler + override fun query(query: DataEntriesQuery, metaData: MetaData): QueryResponseMessage { + + // FIXME, construct query based on the filters. + + val elements = dataEntryRepository.findAll() + val payload = DataEntriesQueryResult(elements = elements.map { it.toDataEntry(objectMapper) }).slice(query = query) + + return QueryResponseMessageResponseType.asQueryResponseMessage( + payload = payload, + metaData = getMaxRevision(elements.filter { dataEntryEntity -> + payload.elements.map { dataEntry -> dataEntry.entryType to dataEntry.entryId } + .contains(dataEntryEntity.dataEntryId.entryType to dataEntryEntity.dataEntryId.entryId) + }.map { it.revision }).toMetaData() + ) + + } + + /** + * Creates new data entry. + */ + @Suppress("unused") + @EventHandler + fun on(event: DataEntryCreatedEvent, metaData: MetaData) { + val entity = event.toEntity(objectMapper, RevisionValue.fromMetaData(metaData)) + // make sure authorizations exist + authorizationPrincipalRepository.saveAll(entity.authorizedPrincipals) + logger.debug { "JPA-VIEW-41: Business data entry created $event." } + dataEntryRepository.save(entity) + updateDataEntryQuery(entity = entity) + } + + + /** + * Updates data entry. + */ + @Suppress("unused") + @EventHandler + fun on(event: DataEntryUpdatedEvent, metaData: MetaData) { + val savedEntity = dataEntryRepository.findByIdOrNull(DataEntryId(entryType = event.entryType, entryId = event.entryId)) + val entity = event.toEntity(objectMapper, RevisionValue.fromMetaData(metaData), oldEntry = savedEntity) + dataEntryRepository.save(entity) + logger.debug { "JPA-VIEW-42: Business data entry updated $event" } + updateDataEntryQuery(entity = entity) + } + + private fun getMaxRevision(elementRevisions: List): RevisionValue = + elementRevisions.maxByOrNull { it }?.let { RevisionValue(it) } ?: RevisionValue.NO_REVISION + + /** + * Updates query for provided data entry identity. + * @param entity entity to notify about. + */ + private fun updateDataEntryQuery(entity: DataEntryEntity) { + + val entry = entity.toDataEntry(objectMapper) + val revisionValue = RevisionValue(revision = entity.revision) + + logger.debug { "JPA-VIEW-43: Updating query with new element ${entry.identity} with revision $revisionValue" } + + queryUpdateEmitter.emit( + DataEntriesForUserQuery::class.java, + { query -> query.applyFilter(entry) }, + QueryResponseMessageResponseType.asSubscriptionUpdateMessage( + payload = DataEntriesQueryResult(elements = listOf(entry)), + metaData = revisionValue.toMetaData() + ) + ) + queryUpdateEmitter.emit( + DataEntryForIdentityQuery::class.java, + { query -> query.applyFilter(entry) }, + QueryResponseMessageResponseType.asSubscriptionUpdateMessage( + payload = DataEntriesQueryResult(elements = listOf(entry)), + metaData = revisionValue.toMetaData() + ) + ) + queryUpdateEmitter.emit( + DataEntriesQuery::class.java, + { query -> query.applyFilter(entry) }, + QueryResponseMessageResponseType.asSubscriptionUpdateMessage( + payload = DataEntriesQueryResult(elements = listOf(entry)), + metaData = revisionValue.toMetaData() + ) + ) + } +} diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt new file mode 100644 index 000000000..e9d41b3a6 --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt @@ -0,0 +1,6 @@ +package io.holunda.polyflow.view.jpa + +import org.springframework.boot.autoconfigure.SpringBootApplication + +@SpringBootApplication +class TestApplication diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt new file mode 100644 index 000000000..c75c7a076 --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt @@ -0,0 +1,27 @@ +package io.holunda.polyflow.view.jpa.auth + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Test + +internal class AuthorizationPrincipalIdTest { + + @Test + fun `should construct authorization principal id for group`() { + val id = AuthorizationPrincipalId("GROUP:groupName") + assertThat(id).isEqualTo(AuthorizationPrincipalId(type = AuthorizationPrincipalType.GROUP, name = "groupName")) + } + + @Test + fun `should construct authorization principal id for user`() { + val id = AuthorizationPrincipalId("USER:userName") + assertThat(id).isEqualTo(AuthorizationPrincipalId(type = AuthorizationPrincipalType.USER, name = "userName")) + } + + @Test + fun `should not construct authorization principal id`() { + assertThatThrownBy { AuthorizationPrincipalId("bad string") }.hasMessage("Illegal auth format, expecting :") + } + + +} diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt new file mode 100644 index 000000000..159276cd0 --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt @@ -0,0 +1,22 @@ +package io.holunda.polyflow.view.jpa.data + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Test + +internal class DataEntryIdTest { + + @Test + fun `should construct data entry id `() { + val id = DataEntryId("type:id") + assertThat(id).isEqualTo(DataEntryId(entryType = "type", entryId = "id")) + } + + + @Test + fun `should not construct data entry id`() { + assertThatThrownBy { DataEntryId("bad string") }.hasMessage("Illegal identity format, expecting :") + } + + +} diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt new file mode 100644 index 000000000..6d6246923 --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt @@ -0,0 +1,159 @@ +package io.holunda.polyflow.view.jpa.data + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import io.holunda.camunda.taskpool.api.business.ProcessingType +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal +import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipalRepository +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.repository.findByIdOrNull +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.junit4.SpringRunner +import java.time.Instant +import java.util.* +import javax.persistence.EntityManager + +@RunWith(SpringRunner::class) +@DataJpaTest +@ActiveProfiles("itest") +internal class DataEntryRepositoryITest { + @Autowired + lateinit var entityManager: EntityManager + + @Autowired + lateinit var dataEntryRepository: DataEntryRepository + + @Autowired + lateinit var authorizationPrincipalRepository: AuthorizationPrincipalRepository + + lateinit var dataEntry: DataEntryEntity + lateinit var dataEntry2: DataEntryEntity + + @Before + fun `insert principals`() { + authorizationPrincipalRepository.saveAll( + setOf( + AuthorizationPrincipal.group("muppets"), + AuthorizationPrincipal.user("kermit"), + AuthorizationPrincipal.user("piggy"), + AuthorizationPrincipal.group("avengers"), + ) + ) + + val id = UUID.randomUUID().toString() + val payload = mapOf( + "amount" to 90L, + "id" to id + ) + + val json = jacksonObjectMapper().writeValueAsString(payload) + + val id2 = UUID.randomUUID().toString() + val state = ProcessingType.IN_PROGRESS.of("In progress") + + dataEntry = DataEntryEntity( + dataEntryId = DataEntryId(entryType = "test", entryId = id), + type = "Test Entry", + name = "Test Case", + applicationName = "my-application", + state = DataEntryStateEmbeddable(state), + description = "This is a test case.", + revision = 1L, + lastModifiedDate = Instant.now(), + authorizedPrincipals = setOf( + AuthorizationPrincipal.group("muppets"), + AuthorizationPrincipal.user("kermit"), + AuthorizationPrincipal.user("piggy"), + ), + payload = json + ).apply { + this.protocol = listOf( + ProtocolElement( + dataEntry = this, + state = DataEntryStateEmbeddable(state), + username = "kermit", + logMessage = "Created", + logDetails = "Created test case." + ) + ) + } + + dataEntry2 = DataEntryEntity( + dataEntryId = DataEntryId(entryType = "test", entryId = id2), + type = "Test Entry", + name = "Test Case 2", + applicationName = "my-application", + state = DataEntryStateEmbeddable(state), + revision = 12L, + description = "This is a second test case.", + lastModifiedDate = Instant.now(), + authorizedPrincipals = setOf( + AuthorizationPrincipal.group("avengers"), + AuthorizationPrincipal.user("piggy"), + ) + ).apply { + this.protocol = listOf( + ProtocolElement( + dataEntry = this, + state = DataEntryStateEmbeddable(state), + username = "ironman", + logMessage = "Created other", + logDetails = "Created test case 2." + ) + ) + } + + entityManager.persist(dataEntry) + entityManager.persist(dataEntry2) + + entityManager.flush() + } + + @After + fun `remove principals`() { + dataEntryRepository.deleteAll() + authorizationPrincipalRepository.deleteAll() + entityManager.flush() + } + + @Test + fun `should find data entry by id`() { + val found = dataEntryRepository.findByIdOrNull(DataEntryId(entryType = dataEntry.dataEntryId.entryType, entryId = dataEntry.dataEntryId.entryId)) + assertThat(found).isNotNull + assertThat(found).isEqualTo(dataEntry) + } + + @Test + fun `should find data entries by authorization`() { + val muppets = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.group("muppets"))) + assertThat(muppets).containsExactly(dataEntry) + + val kermit = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.user("kermit"))) + assertThat(kermit).containsExactly(dataEntry) + + val piggy = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.user("piggy"))) + assertThat(piggy).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) + + val avengers = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.group("avengers"))) + assertThat(avengers).containsExactly(dataEntry2) + + val unknownGroup = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.group("unknown group"))) + assertThat(unknownGroup).isEmpty() + + val unknownUser = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.user("unknown user"))) + assertThat(unknownUser).isEmpty() + } + + @Test + fun `should find all data entries`() { + val all = dataEntryRepository.findAll() + assertThat(all).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) + } + + +} diff --git a/view/jpa/src/test/resources/application-itest.yaml b/view/jpa/src/test/resources/application-itest.yaml new file mode 100644 index 000000000..71344dba7 --- /dev/null +++ b/view/jpa/src/test/resources/application-itest.yaml @@ -0,0 +1,12 @@ +spring: + jpa: + open-in-view: true # disable JPA warning + show-sql: true + +logger: + level: + org.hibernate.type: TRACE + org.hibernate.engine.internal.cascade: DEBUG + org.springframework: INFO + org.springframework.beans: INFO + diff --git a/view/jpa/src/test/resources/banner.txt b/view/jpa/src/test/resources/banner.txt new file mode 100644 index 000000000..fbed1d171 --- /dev/null +++ b/view/jpa/src/test/resources/banner.txt @@ -0,0 +1,3 @@ +_______________________________________________________________________________ +JPA View I-Test +_______________________________________________________________________________ diff --git a/view/jpa/src/test/resources/logback.xml b/view/jpa/src/test/resources/logback.xml new file mode 100644 index 000000000..e32451612 --- /dev/null +++ b/view/jpa/src/test/resources/logback.xml @@ -0,0 +1,11 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + diff --git a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt index 98e112a4a..584c36be3 100755 --- a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt +++ b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt @@ -43,7 +43,7 @@ import javax.annotation.PreDestroy @Component @ProcessingGroup(MongoViewService.PROCESSING_GROUP) class MongoViewService( - private val properties: io.holunda.polyflow.view.mongo.TaskPoolMongoViewProperties, + private val properties: TaskPoolMongoViewProperties, private val taskRepository: TaskRepository, private val dataEntryRepository: DataEntryRepository, private val taskWithDataEntriesRepository: TaskWithDataEntriesRepository, diff --git a/view/pom.xml b/view/pom.xml index 1c2059dab..67fb4763a 100755 --- a/view/pom.xml +++ b/view/pom.xml @@ -22,6 +22,7 @@ view-api simple + jpa mongo form-url-resolver From f0dd88751438608f2e789dfb062e15fbced2c9e4 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Mon, 2 Aug 2021 22:43:29 +0200 Subject: [PATCH 13/27] fix #404: deprecated view annotations, fix docs --- docs/reference-guide/components/index.md | 1 + docs/reference-guide/components/view-jpa.md | 58 +++++++++++++++++++ docs/reference-guide/components/view-mongo.md | 10 ++-- .../reference-guide/components/view-simple.md | 10 ++-- .../view/mongo/MongoViewConfiguration.kt | 7 ++- .../view/simple/SimpleViewConfiguration.kt | 4 +- .../SingleNodeExampleProcessApplication.kt | 3 +- mkdocs.yml | 1 + .../view/mongo/EnableTaskPoolMongoView.kt | 10 +++- .../view/simple/EnableTaskPoolSimpleView.kt | 12 +++- 10 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 docs/reference-guide/components/view-jpa.md diff --git a/docs/reference-guide/components/index.md b/docs/reference-guide/components/index.md index 312c6e836..1dbcf8a38 100644 --- a/docs/reference-guide/components/index.md +++ b/docs/reference-guide/components/index.md @@ -44,6 +44,7 @@ View Components are responsible for creation of a unified read-only projection o user tasks and business data items. They are typically deployed as a part of the process platform. * [In-Memory View](view-simple.md) +* [JPA View](view-jpa.md) * [Mongo DB View](view-mongo.md) * [Property Form URL Resolver](view-form-url-resolver.md) diff --git a/docs/reference-guide/components/view-jpa.md b/docs/reference-guide/components/view-jpa.md new file mode 100644 index 000000000..6d104f97c --- /dev/null +++ b/docs/reference-guide/components/view-jpa.md @@ -0,0 +1,58 @@ +--- +title: JPA View +pageId: view-jpa +--- + +## JPA View + +### Purpose + +The JPA View is component responsible for creating read-projections of tasks and business data entries. It implements +the Taskpool and Datapool View API and persists the projection as document collections in a RDBMS using JPA. It is a useful +if the JPA persistence is used in the project already. + +### Features + +* stores representation of enriched tasks, process definitions and business data entries +* provides single query API + + +### Configuration options + +In order to activate the JPA View implementation, please include the following dependency on your classpath: + +```xml + + io.holunda.polyflow + polyflow-view-jpa + ${polyflow.version} + +``` + +The implementation relies on Spring Data JPA and needs to activate those. + +In addition, configure a a JPA connection to database using `application.properties` or `application.yaml`: + +```yml +spring: + data: + jpa: +``` + +The view implementation provides runtime details using standard logging facility. If you +want to increase the logging level, please setup it e.g. in your `application.yaml`: + + +```yml +logging.level.io.holunda.polyflow.view.jpa: DEBUG +``` + +### Tables + +The JPA View uses several tables to store the results. These are: + +* AUTHORIZATION_PRINCIPAL: table for authorization principals (users, groups) +* DATA_ENTRY: table for business data entries +* DATA_ENTRY_AUTHORIZATIONS: table for authorization information of data entries (relation) +* PROTOCOL_ENTRY: table for data entry protocol entry (users, groups) +* TRACKING_TOKEN: table for Axon Tracking Tokens diff --git a/docs/reference-guide/components/view-mongo.md b/docs/reference-guide/components/view-mongo.md index ce5767322..807560dca 100644 --- a/docs/reference-guide/components/view-mongo.md +++ b/docs/reference-guide/components/view-mongo.md @@ -24,9 +24,9 @@ In order to activate the Mongo implementation, please include the following depe ```xml - io.holunda.taskpool - camunda-bpm-taskpool-view-mongo - ${taskpool.version} + io.holunda.polyflow + polyflow-view-mongo + ${polyflow.version} ``` @@ -35,7 +35,7 @@ the following annotation to any class marked as Spring Configuration loaded duri ```java @Configuration -@EnableTaskPoolMongoView +@EnablePolyflowMongoView @Import({ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration.class, org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration.class, @@ -64,7 +64,7 @@ want to increase the logging level, please setup it e.g. in your `application.ya ```yml -logging.level.io.holunda.camunda.taskpool.view.mongo: DEBUG +logging.level.io.holunda.polyflow.view.mongo: DEBUG ``` diff --git a/docs/reference-guide/components/view-simple.md b/docs/reference-guide/components/view-simple.md index d504a52cb..1591b7cd8 100644 --- a/docs/reference-guide/components/view-simple.md +++ b/docs/reference-guide/components/view-simple.md @@ -23,9 +23,9 @@ In order to activate the in-memory implementation, please include the following ```xml - io.holunda.taskpool - camunda-bpm-taskpool-view-simple - ${taskpool.version} + io.holunda.polyflow + polyflow-view-simple + ${polyflow.version} ``` @@ -34,7 +34,7 @@ loaded during initialization: ```java @Configuration -@EnableTaskPoolSimpleView +@EnablePolyflowSimpleView public class MyViewConfiguration { } @@ -44,5 +44,5 @@ The view implementation provides runtime details using standard logging facility want to increase the logging level, please setup it e.g. in your `application.yaml`: ```yml -logging.level.io.holunda.camunda.taskpool.view.simple: DEBUG +logging.level.io.holunda.polyflow.view.simple: DEBUG ``` diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/mongo/MongoViewConfiguration.kt b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/mongo/MongoViewConfiguration.kt index 7f2a51c1f..00a78b303 100644 --- a/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/mongo/MongoViewConfiguration.kt +++ b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/mongo/MongoViewConfiguration.kt @@ -1,18 +1,19 @@ package io.holunda.camunda.taskpool.example.process.view.mongo -import io.holunda.polyflow.view.mongo.EnableTaskPoolMongoView +import io.holunda.polyflow.view.mongo.EnablePolyflowMongoView import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import org.springframework.context.annotation.Profile @Configuration @Profile("mongo") -@io.holunda.polyflow.view.mongo.EnableTaskPoolMongoView +@EnablePolyflowMongoView @Import( value = [ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration::class, org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration::class, org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration::class, org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration::class - ]) + ] +) class MongoViewConfiguration diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt index a2f4896fb..fe2bca40c 100644 --- a/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt +++ b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt @@ -1,10 +1,10 @@ package io.holunda.camunda.taskpool.example.process.view.simple -import io.holunda.polyflow.view.simple.EnableTaskPoolSimpleView +import io.holunda.polyflow.view.mongo.EnablePolyflowMongoView import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Profile @Configuration @Profile("!mongo") -@io.holunda.polyflow.view.simple.EnableTaskPoolSimpleView +@EnablePolyflowMongoView class SimpleViewConfiguration diff --git a/examples/scenarios/single-node/src/main/kotlin/io/holunda/camunda/taskpool/example/process/SingleNodeExampleProcessApplication.kt b/examples/scenarios/single-node/src/main/kotlin/io/holunda/camunda/taskpool/example/process/SingleNodeExampleProcessApplication.kt index a2ea25f69..374cf0f6e 100755 --- a/examples/scenarios/single-node/src/main/kotlin/io/holunda/camunda/taskpool/example/process/SingleNodeExampleProcessApplication.kt +++ b/examples/scenarios/single-node/src/main/kotlin/io/holunda/camunda/taskpool/example/process/SingleNodeExampleProcessApplication.kt @@ -6,6 +6,7 @@ import io.holunda.camunda.taskpool.core.EnableTaskPool import io.holunda.camunda.taskpool.example.tasklist.EnableTasklist import io.holunda.camunda.taskpool.example.users.EnableExampleUsers import io.holunda.polyflow.urlresolver.EnablePropertyBasedFormUrlResolver +import io.holunda.polyflow.view.simple.EnablePolyflowSimpleView import org.axonframework.commandhandling.CommandMessage import org.axonframework.messaging.correlation.CorrelationDataProvider import org.axonframework.messaging.correlation.MessageOriginProvider @@ -32,7 +33,7 @@ fun main(args: Array) { */ @SpringBootApplication @EnableExampleUsers -@io.holunda.polyflow.view.simple.EnableTaskPoolSimpleView +@EnablePolyflowSimpleView @EnableTaskPool @EnableDataPool @EnableTasklist diff --git a/mkdocs.yml b/mkdocs.yml index fdb3d8cf2..2682dd21c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -114,6 +114,7 @@ nav: - Datapool Core: reference-guide/components/core-datapool.md - View Components: - In-Memory View: reference-guide/components/view-simple.md + - JPA View: reference-guide/components/view-jpa.md - Mongo DB View: reference-guide/components/view-mongo.md - Cockpit View: reference-guide/components/view-cockpit.md - Form URL Resolver: reference-guide/components/view-form-url-resolver.md diff --git a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/EnableTaskPoolMongoView.kt b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/EnableTaskPoolMongoView.kt index ebc35b095..ab57ccaa6 100755 --- a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/EnableTaskPoolMongoView.kt +++ b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/EnableTaskPoolMongoView.kt @@ -6,5 +6,13 @@ import org.springframework.context.annotation.Import * Enables polyflow projection using Mongo DB as persistence. */ @MustBeDocumented -@Import(io.holunda.polyflow.view.mongo.TaskPoolMongoViewConfiguration::class) +@Deprecated(message = "Please use EnablePolyflowMongoView instead", replaceWith = ReplaceWith("EnablePolyflowMongoView")) +@Import(TaskPoolMongoViewConfiguration::class) annotation class EnableTaskPoolMongoView + +/** + * Enables polyflow projection using Mongo DB as persistence. + */ +@MustBeDocumented +@Import(TaskPoolMongoViewConfiguration::class) +annotation class EnablePolyflowMongoView diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/EnableTaskPoolSimpleView.kt b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/EnableTaskPoolSimpleView.kt index 1a44a5b65..08b253a56 100755 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/EnableTaskPoolSimpleView.kt +++ b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/EnableTaskPoolSimpleView.kt @@ -3,8 +3,16 @@ package io.holunda.polyflow.view.simple import org.springframework.context.annotation.Import /** - * Enables simple taskpool view + * Enables simple taskpool view. */ @MustBeDocumented -@Import(io.holunda.polyflow.view.simple.TaskPoolSimpleViewConfiguration::class) +@Deprecated(message = "Please use EnablePolyflowSimpleView instead", replaceWith = ReplaceWith("EnablePolyflowSimpleView")) +@Import(TaskPoolSimpleViewConfiguration::class) annotation class EnableTaskPoolSimpleView + +/** + * Enables simple (in-memory) polyflow view. + */ +@MustBeDocumented +@Import(TaskPoolSimpleViewConfiguration::class) +annotation class EnablePolyflowSimpleView From 90196e9ed5f4c0503e2bfb621bfe2e087f55a7c2 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Wed, 4 Aug 2021 19:14:13 +0200 Subject: [PATCH 14/27] implemented JPA view --- bom/parent/pom.xml | 6 + .../src/main/kotlin/VariableSerializer.kt | 65 +++++ .../src/test/kotlin/JsonPathWithValueTest.kt | 137 +++++++++++ .../src/test/kotlin/TestFixtures.kt | 30 +++ .../src/test/kotlin/VariableSerializerTest.kt | 29 +-- view/filtering/pom.xml | 46 ++++ .../holunda/polyflow/view}/filter/Filter.kt | 73 ++++-- .../view}/sort/DataEntryComparator.kt | 5 +- .../io/holunda/polyflow/view}/sort/Sorter.kt | 10 +- .../sort/TasksWithDataEntriesComparator.kt | 4 +- .../polyflow/view}/filter/FilterTest.kt | 26 +- .../polyflow/view}/sort/ComparatorTest.kt | 2 +- view/jpa/pom.xml | 14 +- .../view/jpa/JpaPolyflowViewService.kt | 118 +++++---- .../view/jpa/PolyflowJpaViewConfiguration.kt | 2 + .../view/jpa/PolyflowJpaViewProperties.kt | 10 + .../view/jpa/auth/AuthorizationPrincipal.kt | 60 ----- .../auth/AuthorizationPrincipalRepository.kt | 5 - .../view/jpa/data/AuthorizationPrincipal.kt | 22 ++ .../jpa/data/AuthorizationPrincipalType.kt | 9 + .../polyflow/view/jpa/data/Converters.kt | 87 ++++--- .../polyflow/view/jpa/data/DataEntryEntity.kt | 61 ++--- .../polyflow/view/jpa/data/DataEntryId.kt | 35 +++ .../view/jpa/data/DataEntryRepository.kt | 58 ++++- .../polyflow/view/jpa/data/ProtocolElement.kt | 24 +- .../polyflow/view/jpa/data/Specification.kt | 89 +++++++ .../main/resources/META-INF/persistence.xml | 1 - .../view/jpa/JpaPolyflowViewServiceITest.kt | 232 ++++++++++++++++++ .../polyflow/view/jpa/TestApplication.kt | 16 +- .../holunda/polyflow/view/jpa/TestFixtures.kt | 8 + .../jpa/auth/AuthorizationPrincipalIdTest.kt | 27 -- .../jpa/data/AuthorizationPrincipalTest.kt | 29 +++ .../view/jpa/data/DataEntryRepositoryITest.kt | 128 +++++++--- .../view/jpa/data/SpecificationTest.kt | 43 ++++ .../src/test/resources/application-itest.yaml | 12 +- view/jpa/src/test/resources/banner.txt | 6 +- view/jpa/src/test/resources/logback.xml | 3 +- view/pom.xml | 1 + view/simple/pom.xml | 4 + .../simple/service/SimpleDataEntryService.kt | 8 +- .../SimpleServiceViewProcessingGroup.kt | 2 +- .../simple/service/SimpleTaskPoolService.kt | 8 +- ... => TaskWithDataEntriesCorrelationTest.kt} | 2 +- 43 files changed, 1192 insertions(+), 365 deletions(-) create mode 100644 integration/common/variable-serializer/src/test/kotlin/JsonPathWithValueTest.kt create mode 100644 integration/common/variable-serializer/src/test/kotlin/TestFixtures.kt create mode 100644 view/filtering/pom.xml rename view/{simple/src/main/kotlin/io/holunda/polyflow/view/simple => filtering/src/main/kotlin/io/holunda/polyflow/view}/filter/Filter.kt (75%) rename view/{simple/src/main/kotlin/io/holunda/polyflow/view/simple => filtering/src/main/kotlin/io/holunda/polyflow/view}/sort/DataEntryComparator.kt (89%) rename view/{simple/src/main/kotlin/io/holunda/polyflow/view/simple => filtering/src/main/kotlin/io/holunda/polyflow/view}/sort/Sorter.kt (90%) rename view/{simple/src/main/kotlin/io/holunda/polyflow/view/simple => filtering/src/main/kotlin/io/holunda/polyflow/view}/sort/TasksWithDataEntriesComparator.kt (89%) rename view/{simple/src/test/kotlin/io/holunda/polyflow/view/simple => filtering/src/test/kotlin/io/holunda/polyflow/view}/filter/FilterTest.kt (89%) rename view/{simple/src/test/kotlin/io/holunda/polyflow/view/simple => filtering/src/test/kotlin/io/holunda/polyflow/view}/sort/ComparatorTest.kt (98%) rename view/jpa/src/{test => main}/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt (56%) create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt delete mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt delete mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalType.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Specification.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewServiceITest.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestFixtures.kt delete mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt create mode 100644 view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/SpecificationTest.kt rename view/simple/src/test/kotlin/io/holunda/polyflow/view/{TaskWithDataEntriesTest.kt => TaskWithDataEntriesCorrelationTest.kt} (98%) diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index 6f11f88c6..7501eb96a 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -116,6 +116,12 @@ polyflow-view-api ${project.version} + + io.holunda.polyflow + polyflow-view-filtering + 3.0.3-SNAPSHOT + + io.holunda.polyflow polyflow-view-simple diff --git a/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt b/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt index 588dce084..ec0f5d6ef 100644 --- a/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt +++ b/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt @@ -4,6 +4,8 @@ import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import org.camunda.bpm.engine.variable.VariableMap import org.camunda.bpm.engine.variable.Variables +import java.time.Instant +import java.util.* /** * Serialize variables into a map using provided object mapper. @@ -15,3 +17,66 @@ fun serialize(payload: Any, mapper: ObjectMapper): VariableMap { this.putAll(mapper.convertValue(payload, object : TypeReference>() {})) } } + +/** + * Deserializes JSON back into variable map. + */ +fun String?.toPayloadVariableMap(objectMapper: ObjectMapper): VariableMap = Variables.createVariables().apply { + if (this@toPayloadVariableMap != null) { + putAll(objectMapper.readValue(this@toPayloadVariableMap, object : TypeReference>() {})) + } +} + +/** + * Serializes payload as JSON. + */ +fun VariableMap.toPayloadJson(objectMapper: ObjectMapper): String = + objectMapper.writeValueAsString(this) + + + +/** + * Converts a deep map structure representing the payload into a map of one level keyed by the JSON path and valued by the value. + * The map might contain primitive types or maps as value. + * @param limit of levels to convert. Defaults to -1 meaning there is no limit. + */ +fun VariableMap.toJsonPathsWithValues(limit: Int = -1): Map { + val pathsWithValues: List> = this.entries.map { + it.toJsonPathWithValue(prefix = "", limit = limit).toMap().toMutableMap() + } + return pathsWithValues.reduce { result, memberList -> result.apply { putAll(memberList) } }.toMap() +} + + +internal fun MutableMap.MutableEntry.toJsonPathWithValue(prefix: String = "", limit: Int = -1): List> { + // level limit check + val currentLevel = prefix.count { ".".contains(it) } + if (limit != -1 && currentLevel >= limit) { + return listOf() + } + // compose the path key + val key = if (prefix == "") { + this.key + } else { + "$prefix.${this.key}" + } + return if (this.value.isPrimitiveType()) { + listOf(key to this.value) + } else if (this.value is Map<*, *>) { + @Suppress("UNCHECKED_CAST") + (this.value as Map).toMutableMap().entries.map { it.toJsonPathWithValue(key, limit) }.flatten() + } else { + // ignore complex objects + listOf() + } +} + +internal fun Any.isPrimitiveType(): Boolean { + return when (this) { + // TODO: ask Jackson for the supported list of types + is String, is Boolean, is Number, is Int, is Long, is Float, is Date, is Instant -> true + else -> false + } +} + + diff --git a/integration/common/variable-serializer/src/test/kotlin/JsonPathWithValueTest.kt b/integration/common/variable-serializer/src/test/kotlin/JsonPathWithValueTest.kt new file mode 100644 index 000000000..63e5bd970 --- /dev/null +++ b/integration/common/variable-serializer/src/test/kotlin/JsonPathWithValueTest.kt @@ -0,0 +1,137 @@ +package io.holunda.polyflow.variable.serializer + +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import io.holunda.camunda.variable.serializer.toJsonPathsWithValues +import org.assertj.core.api.Assertions.assertThat +import org.camunda.bpm.engine.variable.Variables.createVariables +import org.junit.Before +import org.junit.Test +import java.text.SimpleDateFormat +import java.time.Instant +import java.util.* + +internal class JsonPathWithValueTest { + + private val now = Date.from(Instant.now()) + private val mapper = jacksonObjectMapper() + + @Before + fun `setup jackson`() { + mapper.dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'") + mapper.registerModule(JavaTimeModule()) + mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + } + + @Test + fun `should convert map of depth 1 with primitives`() { + val payload = createVariables() + .putValue("key-string", "value") + .putValue("key-long", 19L) + .putValue("key-date", now) + .putValue("key-int", 56) + .putValue("key-bool", true) + .putValue("key-float", 101.1F) + + val result = payload.toJsonPathsWithValues(limit = -1) + + assertThat(result.keys).containsExactlyInAnyOrderElementsOf(payload.keys) + payload.entries.forEach { + assertThat(result).containsKey(it.key) + assertThat(result[it.key]).isEqualTo(it.value) + } + } + + @Test + fun `should convert a deep map with primitives`() { + val flat = mapOf( + "key-string" to "value", + "key-long" to 19L, + "key-date" to now, + "key-int" to 56, + "key-bool" to true + ) + val deep = createVariables().apply { + putAll(flat) + putValue( + "key-map", mapOf( + "child1" to "string", + "child2" to mapOf( + "grand-child1" to "grand-child-value", + "grand-child2" to 451.01F + ) + ) + ) + } + + val result = deep.toJsonPathsWithValues(limit = -1) + + assertThat(result.keys).containsExactlyInAnyOrderElementsOf( + flat.keys.plus( + listOf( + "key-map.child1", + "key-map.child2.grand-child1", + "key-map.child2.grand-child2" + ) + ) + ) + flat.entries.forEach { + assertThat(result).containsKey(it.key) + assertThat(result[it.key]).isEqualTo(it.value) + } + + assertThat(result["key-map.child1"]).isEqualTo("string") + assertThat(result["key-map.child2.grand-child1"]).isEqualTo("grand-child-value") + assertThat(result["key-map.child2.grand-child2"]).isEqualTo(451.01F) + } + + @Test + fun `should convert a deep map with primitives limited by level`() { + val flat = mapOf( + "key-string" to "value", + "key-long" to 19L, + "key-date" to now, + "key-int" to 56, + "key-bool" to true + ) + val deep = createVariables().apply { + putAll(flat) + putValue( + "key-map", mapOf( + "child1" to "string", + "child2" to mapOf( + "grand-child1" to "grand-child-value", + "grand-child2" to 451.01F + ) + ) + ) + } + + val result = deep.toJsonPathsWithValues(limit = 1) + + assertThat(result.keys).containsExactlyInAnyOrderElementsOf( + flat.keys.plus( + listOf( + "key-map.child1" + ) + ) + ) + flat.entries.forEach { + assertThat(result).containsKey(it.key) + assertThat(result[it.key]).isEqualTo(it.value) + } + assertThat(result["key-map.child1"]).isEqualTo("string") + assertThat(result["key-map.child2"]).isNull() + } + + @Test + fun `should ignore complex object`() { + val payload = createVariables().apply { + put("key", Pojo1("value", listOf(Pojo2("value", listOf())))) + } + assertThat(payload.toJsonPathsWithValues()).isEmpty() + } + +} diff --git a/integration/common/variable-serializer/src/test/kotlin/TestFixtures.kt b/integration/common/variable-serializer/src/test/kotlin/TestFixtures.kt new file mode 100644 index 000000000..382ccac80 --- /dev/null +++ b/integration/common/variable-serializer/src/test/kotlin/TestFixtures.kt @@ -0,0 +1,30 @@ +package io.holunda.polyflow.variable.serializer + +import java.time.Instant +import java.time.OffsetDateTime + +data class Pojo1( + val key: String, + val anotherKey: List +) + +data class Pojo2( + val keyZUZUZ: String, + var children: List = listOf() +) + +data class Pojo3( + val key: String, + val anotherKey: Int +) + +data class Pojo4( + val key: String, + val anotherKey: List +) + +data class Pojo5( + val key: String, + val ts: Instant, + val date: OffsetDateTime +) diff --git a/integration/common/variable-serializer/src/test/kotlin/VariableSerializerTest.kt b/integration/common/variable-serializer/src/test/kotlin/VariableSerializerTest.kt index e33cee18d..16c489744 100755 --- a/integration/common/variable-serializer/src/test/kotlin/VariableSerializerTest.kt +++ b/integration/common/variable-serializer/src/test/kotlin/VariableSerializerTest.kt @@ -1,3 +1,4 @@ +package io.holunda.polyflow.variable.serializer import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper @@ -10,7 +11,7 @@ import java.time.Instant import java.time.OffsetDateTime import java.time.ZoneOffset -class SimpleDataEntryCommandSenderTest { +class VariableSerializerTest { private val mapper = jacksonObjectMapper() @@ -120,29 +121,3 @@ class SimpleDataEntryCommandSenderTest { assertThat(elements).containsExactly(linkedMapOf(Pojo2::keyZUZUZ.name to "p2", Pojo2::children.name to listOf())) } } - -data class Pojo1( - val key: String, - val anotherKey: List -) - -data class Pojo2( - val keyZUZUZ: String, - var children: List = listOf() -) - -data class Pojo3( - val key: String, - val anotherKey: Int -) - -data class Pojo4( - val key: String, - val anotherKey: List -) - -data class Pojo5( - val key: String, - val ts: Instant, - val date: OffsetDateTime -) diff --git a/view/filtering/pom.xml b/view/filtering/pom.xml new file mode 100644 index 000000000..7231100e4 --- /dev/null +++ b/view/filtering/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + io.holunda.polyflow + polyflow-view-parent + 3.0.3-SNAPSHOT + + + polyflow-view-filtering + core/view/${project.artifactId} + + + + io.holunda.polyflow + polyflow-view-api + ${project.version} + + + + org.apache.commons + commons-text + 1.9 + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + + junit + junit + + + org.assertj + assertj-core + + + + diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/filter/Filter.kt b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/filter/Filter.kt similarity index 75% rename from view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/filter/Filter.kt rename to view/filtering/src/main/kotlin/io/holunda/polyflow/view/filter/Filter.kt index eb75ad73e..62483fb6d 100755 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/filter/Filter.kt +++ b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/filter/Filter.kt @@ -1,5 +1,6 @@ -package io.holunda.polyflow.view.simple.filter +package io.holunda.polyflow.view.filter +import io.holunda.camunda.taskpool.api.business.DataEntryState import io.holunda.polyflow.view.DataEntry import io.holunda.polyflow.view.Task import io.holunda.polyflow.view.TaskWithDataEntries @@ -61,44 +62,44 @@ internal fun filter(filters: List, values: List): L } /** - * Filters by applying applies predicates on the list of tasks. + * Filters by applying predicates on the list of tasks. */ -internal fun filterByPredicate(values: List, wrapper: TaskPredicateWrapper): List = +fun filterByPredicate(values: List, wrapper: TaskPredicateWrapper): List = values.filter { filterByPredicate(it, wrapper) } /** * Checks if a single task matches the predicates. */ -internal fun filterByPredicate(value: TaskWithDataEntries, wrapper: TaskPredicateWrapper): Boolean = +fun filterByPredicate(value: TaskWithDataEntries, wrapper: TaskPredicateWrapper): Boolean = // no constraints - (wrapper.taskPredicate == null && wrapper.dataEntriesPredicate == null) + (wrapper.taskAttributePredicate == null && wrapper.taskPayloadPredicate == null) // constraint is defined on task and matches on task property - || (wrapper.taskPredicate != null && wrapper.taskPredicate.test(value.task)) + || (wrapper.taskAttributePredicate != null && wrapper.taskAttributePredicate.test(value.task)) // constraint is defined on data and matches on data entry property - || (wrapper.dataEntriesPredicate != null + || (wrapper.taskPayloadPredicate != null && (value.dataEntries .asSequence() .map { dataEntry -> dataEntry.payload } - .find { payload -> wrapper.dataEntriesPredicate.test(payload) } != null || wrapper.dataEntriesPredicate.test(value.task.payload))) + .find { payload -> wrapper.taskPayloadPredicate.test(payload) } != null || wrapper.taskPayloadPredicate.test(value.task.payload))) /** * Checks if a single data entry matches the predicate. */ -internal fun filterByPredicate(value: DataEntry, wrapper: DataEntryPredicateWrapper): Boolean = +fun filterByPredicate(value: DataEntry, wrapper: DataEntryPredicateWrapper): Boolean = // no constraints - (wrapper.dataEntryAttributePredicate == null && wrapper.dataPayloadPredicate == null) + (wrapper.dataEntryAttributePredicate == null && wrapper.dataEntryPayloadPredicate == null) // constraint is defined on data and matches on data entry property || (wrapper.dataEntryAttributePredicate != null && wrapper.dataEntryAttributePredicate.test(value)) // constraint is defined on data payload and matches - || (wrapper.dataPayloadPredicate != null && wrapper.dataPayloadPredicate.test(value.payload)) + || (wrapper.dataEntryPayloadPredicate != null && wrapper.dataEntryPayloadPredicate.test(value.payload)) /** * Constructs data entry predicates */ -internal fun createDataEntryPredicates(criteria: List): DataEntryPredicateWrapper { +fun createDataEntryPredicates(criteria: List): DataEntryPredicateWrapper { val dataEntryPredicates: List> = criteria.toClassAttributePredicates(Criterion.DataEntryCriterion::class) val dataEntryPayloadPredicates: List> = criteria.toPayloadPredicates() @@ -108,7 +109,7 @@ internal fun createDataEntryPredicates(criteria: List): DataEntryPred } else { dataEntryPredicates.reduce { combined, predicate -> combined.or(predicate) } }, - dataPayloadPredicate = if (dataEntryPayloadPredicates.isEmpty()) { + dataEntryPayloadPredicate = if (dataEntryPayloadPredicates.isEmpty()) { null } else { dataEntryPayloadPredicates.reduce { combined, predicate -> combined.or(predicate) } @@ -119,19 +120,19 @@ internal fun createDataEntryPredicates(criteria: List): DataEntryPred /** * Constructs task predicates out of criteria. */ -internal fun createTaskPredicates(criteria: List): TaskPredicateWrapper { - val taskPredicates: List> = criteria.toClassAttributePredicates(Criterion.TaskCriterion::class) - val dataEntryPayloadPredicates: List> = criteria.toPayloadPredicates() +fun createTaskPredicates(criteria: List): TaskPredicateWrapper { + val taskAttributePredicates: List> = criteria.toClassAttributePredicates(Criterion.TaskCriterion::class) + val taskPayloadPredicates: List> = criteria.toPayloadPredicates() return TaskPredicateWrapper( - taskPredicate = if (taskPredicates.isEmpty()) { + taskAttributePredicate = if (taskAttributePredicates.isEmpty()) { null } else { - taskPredicates.reduce { combined, predicate -> combined.or(predicate) } + taskAttributePredicates.reduce { combined, predicate -> combined.or(predicate) } }, - dataEntriesPredicate = if (dataEntryPayloadPredicates.isEmpty()) { + taskPayloadPredicate = if (taskPayloadPredicates.isEmpty()) { null } else { - dataEntryPayloadPredicates.reduce { combined, predicate -> combined.or(predicate) } + taskPayloadPredicates.reduce { combined, predicate -> combined.or(predicate) } }) } @@ -175,7 +176,7 @@ fun List.toPayloadPredicates() = this /** * Forms criteria from string filters. */ -internal fun toCriteria(filters: List) = filters.map { toCriterion(it) }.filter { it !is Criterion.EmptyCriterion } +fun toCriteria(filters: List) = filters.map { toCriterion(it) }.filter { it !is Criterion.EmptyCriterion } /** * Forms a single criteria from string filter. @@ -194,7 +195,7 @@ internal fun toCriterion(filter: String): Criterion { filter.contains(LESS) -> filter.split(LESS).plus(LESS) else -> listOf() } - require(segments.size == 3 && !segments[0].isBlank() && !segments[0].isBlank()) { "Failed to create criteria from $filter." } + require(segments.size == 3 && segments[0].isNotBlank() && segments[0].isNotBlank()) { "Failed to create criteria from $filter." } return when { isTaskAttribute(segments[0]) -> { @@ -223,7 +224,10 @@ internal fun isTaskAttribute(propertyName: String): Boolean = internal fun isDataEntryAttribute(propertyName: String): Boolean = propertyName.startsWith(DATA_PREFIX) && propertyName.length > DATA_PREFIX.length - && DataEntry::class.memberProperties.map { it.name }.contains(propertyName.substring(DATA_PREFIX.length)) + && ( + DataEntry::class.memberProperties.map { it.name }.contains(propertyName.substring(DATA_PREFIX.length)) || + DataEntryState::class.memberProperties.map { "${DataEntry::state.name}.${it.name}" }.contains(propertyName.substring(DATA_PREFIX.length)) + ) /** * Criterion. @@ -274,12 +278,31 @@ sealed class Criterion(open val name: String, open val value: String, open val o /** * Wrapper for a pair of task and data entry predicate. */ -data class TaskPredicateWrapper(val taskPredicate: Predicate?, val dataEntriesPredicate: Predicate?) +data class TaskPredicateWrapper(val taskAttributePredicate: Predicate?, val taskPayloadPredicate: Predicate?) { + /* + * Checks if a payload predicate is set. + */ + fun hasPayloadPredicate() = taskPayloadPredicate != null + + /** + * Checks if attribute predicate is set. + */ + fun hasAttributePredicate() = taskAttributePredicate != null +} /** * Wrapper for a pair of data entry and data payload predicate. */ -data class DataEntryPredicateWrapper(val dataEntryAttributePredicate: Predicate?, val dataPayloadPredicate: Predicate?) +data class DataEntryPredicateWrapper(val dataEntryAttributePredicate: Predicate?, val dataEntryPayloadPredicate: Predicate?) { + /* + * Checks if a payload predicate is set. + */ + fun hasPayloadPredicate() = dataEntryPayloadPredicate != null + /** + * Checks if attribute predicate is set. + */ + fun hasAttributePredicate() = dataEntryAttributePredicate != null +} /** * type of the property diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/DataEntryComparator.kt b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/DataEntryComparator.kt similarity index 89% rename from view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/DataEntryComparator.kt rename to view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/DataEntryComparator.kt index 481d1c454..65ecadb6d 100644 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/DataEntryComparator.kt +++ b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/DataEntryComparator.kt @@ -1,11 +1,12 @@ -package io.holunda.polyflow.view.simple.sort +package io.holunda.polyflow.view.sort import io.holunda.polyflow.view.DataEntry -import io.holunda.polyflow.view.simple.filter.extractValue +import io.holunda.polyflow.view.filter.extractValue import java.lang.reflect.Field import java.util.Comparator import javax.xml.datatype.DatatypeConstants + /** * Comparator of data entries to be used in a sort. */ diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/Sorter.kt b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/Sorter.kt similarity index 90% rename from view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/Sorter.kt rename to view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/Sorter.kt index a50e6727e..8c4b65ee8 100644 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/Sorter.kt +++ b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/Sorter.kt @@ -1,11 +1,11 @@ -package io.holunda.polyflow.view.simple.sort +package io.holunda.polyflow.view.sort import io.holunda.polyflow.view.DataEntry import io.holunda.polyflow.view.Task -import io.holunda.polyflow.view.simple.filter.DATA_PREFIX -import io.holunda.polyflow.view.simple.filter.TASK_PREFIX -import io.holunda.polyflow.view.simple.filter.extractField -import io.holunda.polyflow.view.simple.filter.isTaskAttribute +import io.holunda.polyflow.view.filter.DATA_PREFIX +import io.holunda.polyflow.view.filter.TASK_PREFIX +import io.holunda.polyflow.view.filter.extractField +import io.holunda.polyflow.view.filter.isTaskAttribute import java.lang.reflect.Field import java.time.Instant import java.util.* diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/TasksWithDataEntriesComparator.kt b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/TasksWithDataEntriesComparator.kt similarity index 89% rename from view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/TasksWithDataEntriesComparator.kt rename to view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/TasksWithDataEntriesComparator.kt index 524e1e967..281b7b5e3 100755 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/sort/TasksWithDataEntriesComparator.kt +++ b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/sort/TasksWithDataEntriesComparator.kt @@ -1,7 +1,7 @@ -package io.holunda.polyflow.view.simple.sort +package io.holunda.polyflow.view.sort import io.holunda.polyflow.view.TaskWithDataEntries -import io.holunda.polyflow.view.simple.filter.extractValue +import io.holunda.polyflow.view.filter.extractValue import java.lang.reflect.Field import javax.xml.datatype.DatatypeConstants.LESSER diff --git a/view/simple/src/test/kotlin/io/holunda/polyflow/view/simple/filter/FilterTest.kt b/view/filtering/src/test/kotlin/io/holunda/polyflow/view/filter/FilterTest.kt similarity index 89% rename from view/simple/src/test/kotlin/io/holunda/polyflow/view/simple/filter/FilterTest.kt rename to view/filtering/src/test/kotlin/io/holunda/polyflow/view/filter/FilterTest.kt index 19ec069bb..21b8e205f 100755 --- a/view/simple/src/test/kotlin/io/holunda/polyflow/view/simple/filter/FilterTest.kt +++ b/view/filtering/src/test/kotlin/io/holunda/polyflow/view/filter/FilterTest.kt @@ -1,13 +1,13 @@ -package io.holunda.polyflow.view.simple.filter +package io.holunda.polyflow.view.filter import io.holunda.camunda.taskpool.api.task.ProcessReference import io.holunda.polyflow.view.DataEntry import io.holunda.polyflow.view.Task import io.holunda.polyflow.view.TaskWithDataEntries import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.camunda.bpm.engine.variable.Variables import org.junit.Test -import org.junit.jupiter.api.assertThrows class FilterTest { @@ -107,14 +107,14 @@ class FilterTest { @Test fun `should fail to create criteria`() { - assertThrows { + assertThatThrownBy { toCriteria(listOf("$EQUALS$EQUALS")) } } @Test fun `should fail to create criteria 2`() { - assertThrows { + assertThatThrownBy { toCriteria(listOf("${EQUALS}some$EQUALS")) } } @@ -131,15 +131,15 @@ class FilterTest { val predicates = createTaskPredicates(criteria) assertThat(predicates).isNotNull - assertThat(predicates.taskPredicate).isNotNull - assertThat(predicates.dataEntriesPredicate).isNotNull - - assertThat(predicates.taskPredicate!!.test(task1.task)).isTrue - assertThat(predicates.taskPredicate!!.test(task2.task)).isTrue - assertThat(predicates.taskPredicate!!.test(task3.task)).isFalse - assertThat(predicates.dataEntriesPredicate!!.test(task3.dataEntries[0].payload)).isFalse - assertThat(predicates.dataEntriesPredicate!!.test(task4.dataEntries[0].payload)).isTrue - assertThat(predicates.dataEntriesPredicate!!.test(task5.dataEntries[0].payload)).isTrue + assertThat(predicates.taskAttributePredicate).isNotNull + assertThat(predicates.taskPayloadPredicate).isNotNull + + assertThat(predicates.taskAttributePredicate!!.test(task1.task)).isTrue + assertThat(predicates.taskAttributePredicate!!.test(task2.task)).isTrue + assertThat(predicates.taskAttributePredicate!!.test(task3.task)).isFalse + assertThat(predicates.taskPayloadPredicate!!.test(task3.dataEntries[0].payload)).isFalse + assertThat(predicates.taskPayloadPredicate!!.test(task4.dataEntries[0].payload)).isTrue + assertThat(predicates.taskPayloadPredicate!!.test(task5.dataEntries[0].payload)).isTrue } @Test diff --git a/view/simple/src/test/kotlin/io/holunda/polyflow/view/simple/sort/ComparatorTest.kt b/view/filtering/src/test/kotlin/io/holunda/polyflow/view/sort/ComparatorTest.kt similarity index 98% rename from view/simple/src/test/kotlin/io/holunda/polyflow/view/simple/sort/ComparatorTest.kt rename to view/filtering/src/test/kotlin/io/holunda/polyflow/view/sort/ComparatorTest.kt index a47404913..3e56bab6d 100755 --- a/view/simple/src/test/kotlin/io/holunda/polyflow/view/simple/sort/ComparatorTest.kt +++ b/view/filtering/src/test/kotlin/io/holunda/polyflow/view/sort/ComparatorTest.kt @@ -1,4 +1,4 @@ -package io.holunda.polyflow.view.simple.sort +package io.holunda.polyflow.view.sort import io.holunda.camunda.taskpool.api.task.ProcessReference import io.holunda.polyflow.view.Task diff --git a/view/jpa/pom.xml b/view/jpa/pom.xml index ed33308b4..6f9b364d5 100755 --- a/view/jpa/pom.xml +++ b/view/jpa/pom.xml @@ -17,7 +17,14 @@ io.holunda.polyflow polyflow-view-api - + + io.holunda.polyflow + polyflow-view-filtering + + + io.holunda.polyflow + polyflow-variable-serializer + org.springframework.boot spring-boot-starter-data-jpa @@ -85,6 +92,11 @@ reactor-test test + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + test + diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt similarity index 56% rename from view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt rename to view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt index 97ddebf20..cf265b274 100644 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt @@ -5,14 +5,14 @@ import io.holixon.axon.gateway.query.QueryResponseMessageResponseType import io.holixon.axon.gateway.query.RevisionValue import io.holunda.camunda.taskpool.api.business.DataEntryCreatedEvent import io.holunda.camunda.taskpool.api.business.DataEntryUpdatedEvent +import io.holunda.polyflow.view.filter.Criterion +import io.holunda.polyflow.view.filter.toCriteria import io.holunda.polyflow.view.jpa.JpaPolyflowViewService.Companion.PROCESSING_GROUP -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipalRepository -import io.holunda.polyflow.view.jpa.data.DataEntryEntity -import io.holunda.polyflow.view.jpa.data.DataEntryId -import io.holunda.polyflow.view.jpa.data.DataEntryRepository -import io.holunda.polyflow.view.jpa.data.toEntity -import io.holunda.polyflow.view.jpa.data.toDataEntry +import io.holunda.polyflow.view.jpa.data.* +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.group +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.user +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.isAuthorizedFor +import io.holunda.polyflow.view.query.PageableSortableQuery import io.holunda.polyflow.view.query.data.* import mu.KLogging import org.axonframework.config.ProcessingGroup @@ -28,9 +28,9 @@ import org.springframework.stereotype.Component @ProcessingGroup(PROCESSING_GROUP) class JpaPolyflowViewService( val dataEntryRepository: DataEntryRepository, - val authorizationPrincipalRepository: AuthorizationPrincipalRepository, val objectMapper: ObjectMapper, - val queryUpdateEmitter: QueryUpdateEmitter + val queryUpdateEmitter: QueryUpdateEmitter, + val polyflowJpaViewProperties: PolyflowJpaViewProperties ) : DataEntryApi { companion object : KLogging() { @@ -43,45 +43,50 @@ class JpaPolyflowViewService( require(entryId != null) { "Entry id must be set on query by id" } val elements = dataEntryRepository.findAllById(listOf(DataEntryId(entryId = entryId, entryType = query.entryType))) - val payload = DataEntriesQueryResult(elements = elements.map { it.toDataEntry(objectMapper) }).slice(query = query) - - return QueryResponseMessageResponseType.asQueryResponseMessage( - payload = payload, - metaData = getMaxRevision(elements.filter { dataEntryEntity -> - payload.elements.map { dataEntry -> dataEntry.entryType to dataEntry.entryId } - .contains(dataEntryEntity.dataEntryId.entryType to dataEntryEntity.dataEntryId.entryId) - }.map { it.revision }).toMetaData() - ) + return constructResponse(elements, query) } @QueryHandler override fun query(query: DataEntriesForUserQuery, metaData: MetaData): QueryResponseMessage { - // FIXME, construct query based on the filters. - - val authorizationQuery = setOf( - AuthorizationPrincipal.user(query.user.username), - ).plus(query.user.groups.map { AuthorizationPrincipal.group(it) }) + val authorizedPrincipals: Set = setOf(user(query.user.username)).plus(query.user.groups.map { group(it) }) + val criteria: List = toCriteria(query.filters) + val specification = criteria.toSpecification() - val elements = dataEntryRepository.findAllByAuthorizedPrincipalsIn(authorizationQuery) - val payload = DataEntriesQueryResult(elements = elements.map { it.toDataEntry(objectMapper) }).slice(query = query) - - return QueryResponseMessageResponseType.asQueryResponseMessage( - payload = payload, - metaData = getMaxRevision(elements.filter { dataEntryEntity -> - payload.elements.map { dataEntry -> dataEntry.entryType to dataEntry.entryId } - .contains(dataEntryEntity.dataEntryId.entryType to dataEntryEntity.dataEntryId.entryId) - }.map { it.revision }).toMetaData() - ) + val elements = if (specification != null) { + // alternative + dataEntryRepository.findAll(specification.and(isAuthorizedFor(authorizedPrincipals))) + // dataEntryRepository.findAllByAuthorizedPrincipalsIn(authorizationQuery, specification) + } else { + dataEntryRepository.findAll(isAuthorizedFor(authorizedPrincipals)) + // dataEntryRepository.findAllByAuthorizedPrincipalsIn(authorizedPrincipals.map { it.toString() }) + } + return constructResponse(elements, query) } @QueryHandler override fun query(query: DataEntriesQuery, metaData: MetaData): QueryResponseMessage { - // FIXME, construct query based on the filters. + val criteria: List = toCriteria(query.filters) + val specification = criteria.toSpecification() + val elements = if (specification != null) { + dataEntryRepository.findAll(specification) + } else { + dataEntryRepository.findAll() + } + + return constructResponse(elements, query) + } + + /** + * Constructs response message slicing it. + */ + private fun constructResponse( + elements: Iterable, + query: PageableSortableQuery + ): QueryResponseMessage { - val elements = dataEntryRepository.findAll() val payload = DataEntriesQueryResult(elements = elements.map { it.toDataEntry(objectMapper) }).slice(query = query) return QueryResponseMessageResponseType.asQueryResponseMessage( @@ -91,7 +96,6 @@ class JpaPolyflowViewService( .contains(dataEntryEntity.dataEntryId.entryType to dataEntryEntity.dataEntryId.entryId) }.map { it.revision }).toMetaData() ) - } /** @@ -100,11 +104,23 @@ class JpaPolyflowViewService( @Suppress("unused") @EventHandler fun on(event: DataEntryCreatedEvent, metaData: MetaData) { - val entity = event.toEntity(objectMapper, RevisionValue.fromMetaData(metaData)) - // make sure authorizations exist - authorizationPrincipalRepository.saveAll(entity.authorizedPrincipals) - logger.debug { "JPA-VIEW-41: Business data entry created $event." } - dataEntryRepository.save(entity) + val savedEntity = dataEntryRepository.findByIdOrNull(DataEntryId(entryType = event.entryType, entryId = event.entryId)) + val entity = if (savedEntity == null || savedEntity.lastModifiedDate < event.createModification.time.toInstant()) { + /* + * save the entity only if there is no newer entity in the database (possibly written by another instance of this service in HA setup) + */ + dataEntryRepository.save( + event.toEntity( + objectMapper = objectMapper, + revisionValue = RevisionValue.fromMetaData(metaData), + limit = polyflowJpaViewProperties.payloadAttributeLevelLimit + ) + ).apply { + logger.debug { "JPA-VIEW-41: Business data entry created $event." } + } + } else { + savedEntity + } updateDataEntryQuery(entity = entity) } @@ -116,9 +132,23 @@ class JpaPolyflowViewService( @EventHandler fun on(event: DataEntryUpdatedEvent, metaData: MetaData) { val savedEntity = dataEntryRepository.findByIdOrNull(DataEntryId(entryType = event.entryType, entryId = event.entryId)) - val entity = event.toEntity(objectMapper, RevisionValue.fromMetaData(metaData), oldEntry = savedEntity) - dataEntryRepository.save(entity) - logger.debug { "JPA-VIEW-42: Business data entry updated $event" } + val entity = if (savedEntity == null || savedEntity.lastModifiedDate < event.updateModification.time.toInstant()) { + /* + * save the entity only if there is no newer entity in the database (possibly written by another instance of this service in HA setup) + */ + dataEntryRepository.save( + event.toEntity( + objectMapper = objectMapper, + revisionValue = RevisionValue.fromMetaData(metaData), + oldEntry = savedEntity, + limit = polyflowJpaViewProperties.payloadAttributeLevelLimit + ) + ).apply { + logger.debug { "JPA-VIEW-42: Business data entry updated $event" } + } + } else { + savedEntity + } updateDataEntryQuery(entity = entity) } diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt index 33a1d7972..2d0caeb43 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt @@ -1,8 +1,10 @@ package io.holunda.polyflow.view.jpa import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Configuration @Configuration @EntityScan +@EnableConfigurationProperties(PolyflowJpaViewProperties::class) class PolyflowJpaViewConfiguration diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt new file mode 100644 index 000000000..34aa603c0 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt @@ -0,0 +1,10 @@ +package io.holunda.polyflow.view.jpa + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding + +@ConstructorBinding +@ConfigurationProperties(prefix = "polyflow.view.jpa") +data class PolyflowJpaViewProperties( + val payloadAttributeLevelLimit: Int = -1 +) diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt deleted file mode 100644 index a2223511e..000000000 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipal.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.holunda.polyflow.view.jpa.auth - -import java.io.Serializable -import java.util.* -import javax.persistence.* - -/** - * Authorization principal (user or group). - */ -@Entity(name = "AUTHORIZATION_PRINCIPAL") -class AuthorizationPrincipal( - @EmbeddedId - var id: AuthorizationPrincipalId -) { - companion object { - fun group(name: String) = AuthorizationPrincipal(AuthorizationPrincipalId.invoke("${AuthorizationPrincipalType.GROUP.name}:$name")) - fun user(name: String) = AuthorizationPrincipal(AuthorizationPrincipalId.invoke("${AuthorizationPrincipalType.USER.name}:$name")) - } -} - -/** - * Composite to use for authorization. - */ -@Embeddable -class AuthorizationPrincipalId( - @Column(name = "AUTH_NAME", nullable = false) - var name: String, - @Column(name = "AUTH_TYPE", nullable = false) - @Enumerated(EnumType.STRING) - var type: AuthorizationPrincipalType -) : Serializable { - companion object { - operator fun invoke(auth: String) = auth.split(":").let { - require(it.size == 2) { "Illegal auth format, expecting :" } - AuthorizationPrincipalId(type = AuthorizationPrincipalType.valueOf(it[0]), name = it[1]) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is AuthorizationPrincipalId) return false - return Objects.equals(this.name, other.name) && - Objects.equals(this.type, other.type) - } - - override fun hashCode(): Int { - return Objects.hash(this.name, this.type) - } - - override fun toString(): String = "$type:$name" - -} - -/** - * Authorization type. - */ -enum class AuthorizationPrincipalType { - GROUP, - USER -} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt deleted file mode 100644 index 2a640dd72..000000000 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalRepository.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.holunda.polyflow.view.jpa.auth - -import org.springframework.data.repository.CrudRepository - -interface AuthorizationPrincipalRepository : CrudRepository diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt new file mode 100644 index 000000000..396591aa2 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt @@ -0,0 +1,22 @@ +package io.holunda.polyflow.view.jpa.data + +/** + * DTO representing authorized user or group. + */ +data class AuthorizationPrincipal( + val name: String, + val type: AuthorizationPrincipalType +) { + companion object { + operator fun invoke(auth: String) = auth.split(":").let { + require(it.size == 2) { "Illegal auth format, expecting :" } + AuthorizationPrincipal(type = AuthorizationPrincipalType.valueOf(it[0]), name = it[1]) + } + + fun group(name: String): AuthorizationPrincipal = AuthorizationPrincipal(name = name, type = AuthorizationPrincipalType.GROUP) + fun user(name: String): AuthorizationPrincipal = AuthorizationPrincipal(name = name, type = AuthorizationPrincipalType.USER) + } + + override fun toString(): String = "$type:$name" +} + diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalType.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalType.kt new file mode 100644 index 000000000..9a0967954 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalType.kt @@ -0,0 +1,9 @@ +package io.holunda.polyflow.view.jpa.data + +/** + * Authorization principal type. + */ +enum class AuthorizationPrincipalType { + GROUP, + USER +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt index d6bc971f7..efaf211f5 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt @@ -3,12 +3,15 @@ package io.holunda.polyflow.view.jpa.data import com.fasterxml.jackson.databind.ObjectMapper import io.holixon.axon.gateway.query.RevisionValue import io.holunda.camunda.taskpool.api.business.* +import io.holunda.camunda.variable.serializer.toJsonPathsWithValues +import io.holunda.camunda.variable.serializer.toPayloadJson +import io.holunda.camunda.variable.serializer.toPayloadVariableMap import io.holunda.polyflow.view.DataEntry import io.holunda.polyflow.view.ProtocolEntry -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipalType -import org.camunda.bpm.engine.variable.VariableMap -import org.camunda.bpm.engine.variable.Variables.createVariables +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.group +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.user +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipalType.GROUP +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipalType.USER /** * Converts the entity into API type. @@ -26,37 +29,18 @@ fun DataEntryEntity.toDataEntry(objectMapper: ObjectMapper) = protocol = this.protocol.map { it.toProtocolEntry() }, authorizedUsers = this.authorizedPrincipals.asUsernames(), authorizedGroups = this.authorizedPrincipals.asGroupnames(), - payload = this.payload.toPayloadVariableMap(objectMapper) + payload = this.payload.toPayloadVariableMap(objectMapper), ) /** * Retrieves user authorizations as list of user names. */ -fun Set.asUsernames() = this.filter { it.id.type == AuthorizationPrincipalType.USER }.map { it.id.name }.toSet() +fun Set.asUsernames() = this.filter { AuthorizationPrincipal(it).type == USER }.map { AuthorizationPrincipal(it).name }.toSet() /** * Retrieves group authorizations as list of group names. */ -fun Set.asGroupnames() = this.filter { it.id.type == AuthorizationPrincipalType.GROUP }.map { it.id.name }.toSet() - -/** - * Serializes payload as JSON. - */ -fun VariableMap.toPayloadJson(objectMapper: ObjectMapper): String = - objectMapper.writeValueAsString(this) - -/** - * Deserializes JSON back into variable map. - */ -fun String?.toPayloadVariableMap(objectMapper: ObjectMapper): VariableMap = if (this != null) { - val mapType = objectMapper.typeFactory.constructMapType(Map::class.java, String::class.java, Any::class.java) - val map: Map = objectMapper.convertValue(this, mapType) - createVariables().apply { - putAll(map) - } -} else { - createVariables() -} +fun Set.asGroupnames() = this.filter { AuthorizationPrincipal(it).type == GROUP }.map { AuthorizationPrincipal(it).name }.toSet() /** * Converts entity to API type. @@ -83,12 +67,15 @@ fun DataEntryState.toState() = DataEntryStateEmbeddable(processingType = this.pr /** * Event to entity. */ -fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue) = DataEntryEntity( +fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue, limit: Int) = DataEntryEntity( dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), payload = this.payload.toPayloadJson(objectMapper), + payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toSet(), name = this.name, applicationName = this.applicationName, type = this.type, + createdDate = this.createModification.time.toInstant(), + lastModifiedDate = this.createModification.time.toInstant(), description = this.description, state = this.state.toState(), formKey = this.formKey, @@ -97,8 +84,8 @@ fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } else { 0L }, - authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.user(it) } - .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.group(it) }).toSet(), + authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { user(it).toString() } + .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { group(it).toString() }).toSet(), ).apply { this.protocol = this.protocol.addModification(this, this@toEntity.createModification, this@toEntity.state) } @@ -106,18 +93,21 @@ fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re /** * Event to entity for an update, if an optional entry exists. */ -fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue, oldEntry: DataEntryEntity?) = if (oldEntry == null) { +fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue, oldEntry: DataEntryEntity?, limit: Int) = if (oldEntry == null) { DataEntryEntity( dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), payload = this.payload.toPayloadJson(objectMapper), + payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toSet(), name = this.name, applicationName = this.applicationName, type = this.type, + createdDate = this.updateModification.time.toInstant(), + lastModifiedDate = this.updateModification.time.toInstant(), description = this.description, state = this.state.toState(), formKey = this.formKey, - authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.user(it) } - .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { AuthorizationPrincipal.group(it) }).toSet(), + authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { user(it).toString() } + .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { group(it).toString() }).toSet(), revision = if (revisionValue != RevisionValue.NO_REVISION) { revisionValue.revision } else { @@ -127,21 +117,23 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } else { oldEntry.also { it.payload = this.payload.toPayloadJson(objectMapper) + it.payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toSet() it.name = this.name it.applicationName = this.applicationName it.type = this.type it.description = this.description it.state = this.state.toState() + it.lastModifiedDate = this.updateModification.time.toInstant() it.formKey = this.formKey it.authorizedPrincipals = AuthorizationChange.applyUserAuthorization( it.authorizedPrincipals.asUsernames(), this.authorizations - ).map { AuthorizationPrincipal.user(it) } + ).map { user(it).toString() } .plus(AuthorizationChange.applyGroupAuthorization( it.authorizedPrincipals.asGroupnames(), this.authorizations - ).map { AuthorizationPrincipal.group(it) }) + ).map { group(it).toString() }) .toSet() it.revision = if (revisionValue != RevisionValue.NO_REVISION) { revisionValue.revision @@ -154,16 +146,21 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } /** - * Adds a modification to the protocol. + * Adds a modification to the protocol, if it doesn't exist in the protocol already, comparing all protocol element properties + * besides the technical id. */ fun List.addModification(dataEntry: DataEntryEntity, modification: Modification, state: DataEntryState) = - this.plus( - ProtocolElement( - dataEntry = dataEntry, - time = modification.time.toInstant(), - username = modification.username, - logMessage = modification.log, - logDetails = modification.logNotes, - state = state.toState() - ) - ) + ProtocolElement( + dataEntry = dataEntry, + time = modification.time.toInstant(), + username = modification.username, + logMessage = modification.log, + logDetails = modification.logNotes, + state = state.toState() + ).let { protocolElement -> + if (dataEntry.protocol.any { existing -> existing.same(protocolElement) }) { + this + } else { + this.plus(protocolElement) + } + } diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt index 33bb09f5c..82bb45fca 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt @@ -1,12 +1,12 @@ package io.holunda.polyflow.view.jpa.data -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal -import java.io.Serializable import java.time.Instant -import java.util.* import javax.persistence.* +/** + * Entity to store data entries. + */ @Entity(name = "DATA_ENTRY") class DataEntryEntity( @EmbeddedId @@ -33,22 +33,29 @@ class DataEntryEntity( @Column(name = "DATE_LAST_MODIFIED", nullable = false) var lastModifiedDate: Instant = Instant.now(), - @ManyToMany(fetch = FetchType.EAGER, targetEntity = AuthorizationPrincipal::class) - @JoinTable( + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( name = "DATA_ENTRY_AUTHORIZATIONS", - inverseJoinColumns = [ - JoinColumn(name = "AUTH_NAME", referencedColumnName = "AUTH_NAME"), - JoinColumn(name = "AUTH_TYPE", referencedColumnName = "AUTH_TYPE"), - ], joinColumns = [ JoinColumn(name = "ENTRY_TYPE", referencedColumnName = "ENTRY_TYPE"), JoinColumn(name = "ENTRY_ID", referencedColumnName = "ENTRY_ID"), - ], + ] + ) + @Column(name = "AUTHORIZED_PRINCIPAL", nullable = false) + var authorizedPrincipals: Set = setOf(), - ) - var authorizedPrincipals: Set = setOf(), + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable( + name = "DATA_PAYLOAD_ATTRIBUTES", + joinColumns = [ + JoinColumn(name = "ENTRY_TYPE", referencedColumnName = "ENTRY_TYPE"), + JoinColumn(name = "ENTRY_ID", referencedColumnName = "ENTRY_ID"), + ] + ) + @Column(name = "PAYLOAD_ATTRIBUTE", nullable = false) + var payloadAttributes: Set = setOf(), - @OneToMany(mappedBy = "dataEntry", orphanRemoval = true, cascade = [CascadeType.ALL], targetEntity = ProtocolElement::class) + @OneToMany(mappedBy = "dataEntry", orphanRemoval = true, cascade = [CascadeType.ALL], targetEntity = ProtocolElement::class, fetch = FetchType.EAGER) var protocol: List = mutableListOf(), @Lob @@ -59,31 +66,3 @@ class DataEntryEntity( } } -@Embeddable -class DataEntryId( - @Column(name = "ENTRY_ID", nullable = false) - var entryId: String, - @Column(name = "ENTRY_TYPE", nullable = false) - var entryType: String -) : Serializable { - - companion object { - operator fun invoke(identity: String): DataEntryId = identity.split(":").let { - require(it.size == 2) { "Illegal identity format, expecting :" } - DataEntryId(entryType = it[0], entryId = it[1]) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is DataEntryId) return false - return Objects.equals(this.entryId, other.entryId) && - Objects.equals(this.entryType, other.entryType) - } - - override fun hashCode(): Int { - return Objects.hash(this.entryId, this.entryType) - } - - override fun toString(): String = "$entryType:$entryId" -} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt new file mode 100644 index 000000000..612397b49 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt @@ -0,0 +1,35 @@ +package io.holunda.polyflow.view.jpa.data + +import java.io.Serializable +import java.util.* +import javax.persistence.Column +import javax.persistence.Embeddable + +@Embeddable +class DataEntryId( + @Column(name = "ENTRY_ID", nullable = false) + var entryId: String, + @Column(name = "ENTRY_TYPE", nullable = false) + var entryType: String +) : Serializable { + + companion object { + operator fun invoke(identity: String): DataEntryId = identity.split(":").let { + require(it.size == 2) { "Illegal identity format, expecting :" } + DataEntryId(entryType = it[0], entryId = it[1]) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DataEntryId) return false + return Objects.equals(this.entryId, other.entryId) && + Objects.equals(this.entryType, other.entryType) + } + + override fun hashCode(): Int { + return Objects.hash(this.entryId, this.entryType) + } + + override fun toString(): String = "$entryType:$entryId" +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt index ea25a0d03..f5dd0149a 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt @@ -1,22 +1,72 @@ package io.holunda.polyflow.view.jpa.data import io.holunda.camunda.taskpool.api.business.ProcessingType -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal import org.springframework.data.jpa.domain.Specification import org.springframework.data.jpa.repository.JpaSpecificationExecutor import org.springframework.data.repository.CrudRepository interface DataEntryRepository : CrudRepository, JpaSpecificationExecutor { - fun findAllByAuthorizedPrincipalsIn(authorizedPrincipals: Set): List + fun findAllByAuthorizedPrincipalsIn(authorizedPrincipalIds: Collection): List companion object { + + /** + * Specification for the user-defined state. + */ fun hasState(state: String): Specification = - Specification { dataEntry, _, builder -> builder.equal(dataEntry.get("state").get("state"), state) } + Specification { dataEntry, _, builder -> + builder.equal( + dataEntry.get(DataEntryEntity::state.name).get(DataEntryStateEmbeddable::state.name), + state + ) + } + /** + * Specification for the processing type. + */ fun hasProcessingType(processingType: ProcessingType): Specification = - Specification { dataEntry, _, builder -> builder.equal(dataEntry.get("state").get("processingType"), processingType) } + Specification { dataEntry, _, builder -> + builder.equal( + dataEntry.get(DataEntryEntity::state.name).get(DataEntryStateEmbeddable::processingType.name), + processingType.name + ) + } + + /** + * Specification for checking the payload attribute. + */ + fun hasPayloadAttribute(name: String, value: String): Specification = + Specification { dataEntry, _, builder -> + builder.isMember( + "$name=$value", + dataEntry.get>(DataEntryEntity::payloadAttributes.name) + ) + } + + /** + * Specification for checking authorization. + */ + fun isAuthorizedFor(principal: AuthorizationPrincipal): Specification = + Specification { dataEntry, _, builder -> + builder.isMember( + "${principal.type}:${principal.name}", + dataEntry.get>(DataEntryEntity::authorizedPrincipals.name) + ) + } + /** + * Specification for checking authorization of multiple principals. + */ + fun isAuthorizedFor(principals: Collection): Specification = + composeOr(principals.map { principal -> + Specification { dataEntry, _, builder -> + builder.isMember( + "${principal.type}:${principal.name}", + dataEntry.get>(DataEntryEntity::authorizedPrincipals.name) + ) + } + }) ?: Specification { _, _, _ -> null } } } diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt index 0d1ec130e..ad5d93159 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt @@ -27,4 +27,26 @@ class ProtocolElement( JoinColumn(name = "ENTRY_ID", referencedColumnName = "ENTRY_ID", nullable = false) ) var dataEntry: DataEntryEntity -) +) { + + + /** + * Checks if the protocol element is the same as provided. + */ + fun same(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ProtocolElement + + if (time != other.time) return false + if (state != other.state) return false + if (username != other.username) return false + if (logMessage != other.logMessage) return false + if (logDetails != other.logDetails) return false + if (dataEntry != other.dataEntry) return false + + return true + } + +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Specification.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Specification.kt new file mode 100644 index 000000000..ea820918a --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Specification.kt @@ -0,0 +1,89 @@ +package io.holunda.polyflow.view.jpa.data + +import io.holunda.camunda.taskpool.api.business.DataEntryState +import io.holunda.camunda.taskpool.api.business.ProcessingType +import io.holunda.polyflow.view.DataEntry +import io.holunda.polyflow.view.filter.Criterion +import io.holunda.polyflow.view.filter.EQUALS +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.hasPayloadAttribute +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.hasProcessingType +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.hasState +import org.springframework.data.jpa.domain.Specification +import org.springframework.data.jpa.domain.Specification.where + +/** + * Creates a JPQL specification out of predicate wrapper. + */ +fun List.toSpecification(): Specification? { + + val attributeSpec = toDataEntryAttributeSpecification() + val payloadSpec = toDataEntryPayloadSpecification() + + return when { + attributeSpec != null && payloadSpec != null -> where(attributeSpec).and(payloadSpec) + attributeSpec != null -> attributeSpec + payloadSpec != null -> payloadSpec + else -> null + } +} + +/** + * Specification for query on data entry attributes. + */ +internal fun List.toDataEntryAttributeSpecification(): Specification? { + val relevant = this.filterIsInstance().map { it.toSpecification() } + return composeAnd(relevant) +} + +/** + * Specification on payload. + */ +internal fun List.toDataEntryPayloadSpecification(): Specification? { + val relevant = this.filterIsInstance().map { it.toSpecification() } + return composeAnd(relevant) +} + +/** + * Creates JPA specification for the query of direct attributes of the data entry. + */ +internal fun Criterion.DataEntryCriterion.toSpecification(): Specification { + return when (this.name) { + "${DataEntry::state.name}.${DataEntryState::state.name}" -> hasState(this.value) + "${DataEntry::state.name}.${DataEntryState::processingType.name}" -> hasProcessingType(ProcessingType.valueOf(this.value)) + else -> throw IllegalArgumentException("JPA View found unsupported data entry attribute: ${this.name}.") + } +} + +/** + * Creates JPA Specification for query of payload attributes based on JSON paths. + */ +internal fun Criterion.PayloadEntryCriterion.toSpecification(): Specification { + return when (this.operator) { + EQUALS -> hasPayloadAttribute(this.name, this.value) + else -> throw IllegalArgumentException("JPA View currently supports only equals as operator for filtering.") + } +} + + +/** + * Compose multiple specifications into one specification using conjunction. + */ +internal fun composeAnd(specifications: List?>): Specification? { + return when (specifications.size) { + 0 -> null + 1 -> specifications[0] + else -> where(specifications[0]).and(composeAnd(specifications.subList(1, specifications.size))) + } +} + +/** + * Compose multiple specifications into one specification using disjunction. + */ +internal fun composeOr(specifications: List?>): Specification? { + return when (specifications.size) { + 0 -> null + 1 -> specifications[0] + else -> where(specifications[0]).or(composeOr(specifications.subList(1, specifications.size))) + } +} + diff --git a/view/jpa/src/main/resources/META-INF/persistence.xml b/view/jpa/src/main/resources/META-INF/persistence.xml index c7bfa4126..9ec16c212 100644 --- a/view/jpa/src/main/resources/META-INF/persistence.xml +++ b/view/jpa/src/main/resources/META-INF/persistence.xml @@ -1,6 +1,5 @@ - io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal io.holunda.polyflow.view.jpa.data.DataEntryEntity io.holunda.polyflow.view.jpa.data.ProtocolElement diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewServiceITest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewServiceITest.kt new file mode 100644 index 000000000..a1f2b5c5c --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewServiceITest.kt @@ -0,0 +1,232 @@ +package io.holunda.polyflow.view.jpa + +import com.fasterxml.jackson.databind.ObjectMapper +import io.holunda.camunda.taskpool.api.business.AuthorizationChange.Companion.addGroup +import io.holunda.camunda.taskpool.api.business.AuthorizationChange.Companion.addUser +import io.holunda.camunda.taskpool.api.business.DataEntryCreatedEvent +import io.holunda.camunda.taskpool.api.business.DataEntryUpdatedEvent +import io.holunda.camunda.taskpool.api.business.Modification +import io.holunda.camunda.taskpool.api.business.ProcessingType +import io.holunda.camunda.variable.serializer.serialize +import io.holunda.polyflow.view.DataEntry +import io.holunda.polyflow.view.auth.User +import io.holunda.polyflow.view.query.data.DataEntriesForUserQuery +import io.holunda.polyflow.view.query.data.DataEntriesQuery +import io.holunda.polyflow.view.query.data.DataEntriesQueryResult +import io.holunda.polyflow.view.query.data.DataEntryForIdentityQuery +import org.assertj.core.api.Assertions.assertThat +import org.axonframework.messaging.MetaData +import org.axonframework.queryhandling.QueryResponseMessage +import org.axonframework.queryhandling.QueryUpdateEmitter +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.junit4.SpringRunner +import java.time.Instant +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.temporal.ChronoUnit +import java.util.* + +@RunWith(SpringRunner::class) +@SpringBootTest +@ActiveProfiles("itest") +internal class JpaPolyflowViewServiceITest { + + @MockBean + lateinit var queryUpdateEmitter: QueryUpdateEmitter + + @Autowired + lateinit var jpaPolyflowViewService: JpaPolyflowViewService + + @Autowired + lateinit var objectMapper: ObjectMapper + + private val id = UUID.randomUUID().toString() + private val id2 = UUID.randomUUID().toString() + private val now = Instant.now() + + @Before + internal fun `ingest events`() { + + val payload = mapOf( + "key" to "value", + "key-int" to 1, + "complex" to Pojo( + attribute1 = "value", + attribute2 = Date.from(now) + ) + ) + + jpaPolyflowViewService.on( + event = DataEntryCreatedEvent( + entryType = "io.polyflow.test", + entryId = id, + type = "Test", + applicationName = "test-application", + name = "Test Entry 1", + state = ProcessingType.IN_PROGRESS.of("In progress"), + payload = serialize(payload = payload, mapper = objectMapper), + authorizations = listOf( + addUser("kermit"), + addGroup("muppets") + ), + createModification = Modification( + time = OffsetDateTime.ofInstant(now, ZoneOffset.UTC), + username = "kermit", + log = "Created", + logNotes = "Created the entry" + ) + ), + metaData = MetaData.emptyInstance() + ) + + jpaPolyflowViewService.on( + event = DataEntryUpdatedEvent( + entryType = "io.polyflow.test", + entryId = id, + type = "Test", + applicationName = "test-application", + name = "Test Entry 1", + state = ProcessingType.IN_PROGRESS.of("In review"), + payload = serialize(payload = payload, mapper = objectMapper), + authorizations = listOf( + addUser("ironman"), + ), + updateModification = Modification( + time = OffsetDateTime.ofInstant(now, ZoneOffset.UTC).plus(10, ChronoUnit.SECONDS), + username = "ironman", + log = "Updated", + logNotes = "Updated the entry" + ) + ), + metaData = MetaData.emptyInstance() + ) + + jpaPolyflowViewService.on( + event = DataEntryCreatedEvent( + entryType = "io.polyflow.test", + entryId = id2, + type = "Test", + applicationName = "test-application", + name = "Test Entry 2", + state = ProcessingType.IN_PROGRESS.of("In review"), + payload = serialize(payload = mapOf("key-int" to 2, "key" to "value"), mapper = objectMapper), + authorizations = listOf( + addUser("piggy"), + addGroup("muppets") + ), + createModification = Modification( + time = OffsetDateTime.ofInstant(now, ZoneOffset.UTC), + username = "piggy", + log = "Created", + logNotes = "Created the entry" + ) + ), + metaData = MetaData.emptyInstance() + ) + } + + @After + internal fun `cleanup projection`() { + jpaPolyflowViewService.dataEntryRepository.deleteAll() + } + + @Test + internal fun `should find the entry by id`() { + assertResultIsTestEntry1( + jpaPolyflowViewService.query( + DataEntryForIdentityQuery(entryType = "io.polyflow.test", entryId = id) + ) + ) + } + + @Test + internal fun `should find the entry by user`() { + + assertResultIsTestEntry1( + jpaPolyflowViewService.query( + DataEntriesForUserQuery(user = User("kermit", groups = setOf())) + ) + ) + + assertResultIsTestEntry1And2( + jpaPolyflowViewService.query( + DataEntriesForUserQuery(user = User("superman", groups = setOf("muppets"))) + ) + ) + + assertResultIsTestEntry1( + jpaPolyflowViewService.query( + DataEntriesForUserQuery(user = User("ironman", groups = setOf())) + ) + ) + + assertThat( + jpaPolyflowViewService.query( + DataEntriesForUserQuery(user = User("superman", groups = setOf("avengers"))) + ).payload.elements + ).isEmpty() + + } + + @Test + internal fun `should find the entry by user and filter`() { + assertResultIsTestEntry1( + jpaPolyflowViewService.query( + DataEntriesForUserQuery(user = User("kermit", groups = setOf("muppets")), filters = listOf("key-int=1")) + ) + ) + } + + @Test + internal fun `should find the entry by filter`() { + assertResultIsTestEntry1And2( + jpaPolyflowViewService.query( + DataEntriesQuery(filters = listOf("key=value")) + ) + ) + } + + + internal fun assertResultIsTestEntry1(result: QueryResponseMessage) { + assertThat(result.payload.elements.size).isEqualTo(1) + val dataEntry = result.payload.elements[0] + assertTestDataEntry1(dataEntry) + } + + internal fun assertResultIsTestEntry1And2(result: QueryResponseMessage) { + assertThat(result.payload.elements.size).isEqualTo(2) + assertTestDataEntry1(result.payload.elements[0]) + assertTestDataEntry2(result.payload.elements[1]) + } + + + internal fun assertTestDataEntry1(dataEntry: DataEntry) { + assertThat(dataEntry.entryId).isEqualTo(id) + assertThat(dataEntry.entryType).isEqualTo("io.polyflow.test") + assertThat(dataEntry.name).isEqualTo("Test Entry 1") + assertThat(dataEntry.payload).containsKeys("key", "key-int", "complex") + assertThat(dataEntry.protocol.size).isEqualTo(2) + assertThat(dataEntry.protocol[0].time).isEqualTo(now) + assertThat(dataEntry.protocol[0].username).isEqualTo("kermit") + assertThat(dataEntry.protocol[1].time).isEqualTo(now.plus(10, ChronoUnit.SECONDS)) + assertThat(dataEntry.protocol[1].username).isEqualTo("ironman") + } + + internal fun assertTestDataEntry2(dataEntry: DataEntry) { + assertThat(dataEntry.entryId).isEqualTo(id2) + assertThat(dataEntry.entryType).isEqualTo("io.polyflow.test") + assertThat(dataEntry.name).isEqualTo("Test Entry 2") + assertThat(dataEntry.payload).containsKeys("key-int") + assertThat(dataEntry.protocol.size).isEqualTo(1) + assertThat(dataEntry.protocol[0].time).isEqualTo(now) + assertThat(dataEntry.protocol[0].username).isEqualTo("piggy") + } + +} diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt index e9d41b3a6..2db869490 100644 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestApplication.kt @@ -1,6 +1,20 @@ package io.holunda.polyflow.view.jpa +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.Bean +import java.text.SimpleDateFormat @SpringBootApplication -class TestApplication +class TestApplication { + + @Bean + fun objectMapper() = jacksonObjectMapper().apply { + dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'") + registerModule(JavaTimeModule()) + configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) + } +} diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestFixtures.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestFixtures.kt new file mode 100644 index 000000000..654fab249 --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/TestFixtures.kt @@ -0,0 +1,8 @@ +package io.holunda.polyflow.view.jpa + +import java.util.* + +data class Pojo( + val attribute1: String, + val attribute2: Date +) diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt deleted file mode 100644 index c75c7a076..000000000 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/auth/AuthorizationPrincipalIdTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.holunda.polyflow.view.jpa.auth - -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.Test - -internal class AuthorizationPrincipalIdTest { - - @Test - fun `should construct authorization principal id for group`() { - val id = AuthorizationPrincipalId("GROUP:groupName") - assertThat(id).isEqualTo(AuthorizationPrincipalId(type = AuthorizationPrincipalType.GROUP, name = "groupName")) - } - - @Test - fun `should construct authorization principal id for user`() { - val id = AuthorizationPrincipalId("USER:userName") - assertThat(id).isEqualTo(AuthorizationPrincipalId(type = AuthorizationPrincipalType.USER, name = "userName")) - } - - @Test - fun `should not construct authorization principal id`() { - assertThatThrownBy { AuthorizationPrincipalId("bad string") }.hasMessage("Illegal auth format, expecting :") - } - - -} diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt new file mode 100644 index 000000000..f2dc239ac --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt @@ -0,0 +1,29 @@ +package io.holunda.polyflow.view.jpa.data + +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.group +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.user +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Test + +internal class AuthorizationPrincipalTest { + + @Test + fun `should construct authorization principal id for group`() { + val id = AuthorizationPrincipal("GROUP:groupName") + assertThat(id).isEqualTo(group(name = "groupName")) + } + + @Test + fun `should construct authorization principal id for user`() { + val id = AuthorizationPrincipal("USER:userName") + assertThat(id).isEqualTo(user(name = "userName")) + } + + @Test + fun `should not construct authorization principal id`() { + assertThatThrownBy { AuthorizationPrincipal("bad string") }.hasMessage("Illegal auth format, expecting :") + } + + +} diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt index 6d6246923..b1772653f 100644 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt @@ -2,15 +2,22 @@ package io.holunda.polyflow.view.jpa.data import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.holunda.camunda.taskpool.api.business.ProcessingType -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipal -import io.holunda.polyflow.view.jpa.auth.AuthorizationPrincipalRepository +import io.holunda.camunda.variable.serializer.toJsonPathsWithValues +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.group +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.user +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.hasPayloadAttribute +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.hasProcessingType +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.hasState +import io.holunda.polyflow.view.jpa.data.DataEntryRepository.Companion.isAuthorizedFor import org.assertj.core.api.Assertions.assertThat +import org.camunda.bpm.engine.variable.Variables.createVariables import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.domain.Specification.where import org.springframework.data.repository.findByIdOrNull import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.junit4.SpringRunner @@ -28,54 +35,65 @@ internal class DataEntryRepositoryITest { @Autowired lateinit var dataEntryRepository: DataEntryRepository - @Autowired - lateinit var authorizationPrincipalRepository: AuthorizationPrincipalRepository - lateinit var dataEntry: DataEntryEntity lateinit var dataEntry2: DataEntryEntity @Before - fun `insert principals`() { - authorizationPrincipalRepository.saveAll( - setOf( - AuthorizationPrincipal.group("muppets"), - AuthorizationPrincipal.user("kermit"), - AuthorizationPrincipal.user("piggy"), - AuthorizationPrincipal.group("avengers"), - ) - ) + fun `insert entries`() { val id = UUID.randomUUID().toString() - val payload = mapOf( - "amount" to 90L, - "id" to id - ) - val json = jacksonObjectMapper().writeValueAsString(payload) + val payload1 = createVariables().apply { + putAll( + mapOf( + "amount" to 90L, + "id" to id, + "child" to mapOf( + "key" to "value", + "key-number" to 42 + ) + ) + ) + } + val json = jacksonObjectMapper().writeValueAsString(payload1) + val payloadAttributes = payload1.toJsonPathsWithValues().map { "${it.key}=${it.value}" }.toSet() + + val payload2 = createVariables().apply { + putAll( + mapOf( + "child" to mapOf( + "key-number" to 42 + ) + ) + ) + } + val json2 = jacksonObjectMapper().writeValueAsString(payload2) + val payloadAttributes2 = payload2.toJsonPathsWithValues().map { "${it.key}=${it.value}" }.toSet() - val id2 = UUID.randomUUID().toString() - val state = ProcessingType.IN_PROGRESS.of("In progress") + val state1 = ProcessingType.IN_PROGRESS.of("In progress") + val state2 = ProcessingType.IN_PROGRESS.of("In review") dataEntry = DataEntryEntity( dataEntryId = DataEntryId(entryType = "test", entryId = id), type = "Test Entry", name = "Test Case", applicationName = "my-application", - state = DataEntryStateEmbeddable(state), + state = DataEntryStateEmbeddable(state1), description = "This is a test case.", revision = 1L, lastModifiedDate = Instant.now(), authorizedPrincipals = setOf( - AuthorizationPrincipal.group("muppets"), - AuthorizationPrincipal.user("kermit"), - AuthorizationPrincipal.user("piggy"), + group("muppets").toString(), + user("kermit").toString(), + user("piggy").toString(), ), - payload = json + payload = json, + payloadAttributes = payloadAttributes ).apply { this.protocol = listOf( ProtocolElement( dataEntry = this, - state = DataEntryStateEmbeddable(state), + state = DataEntryStateEmbeddable(state1), username = "kermit", logMessage = "Created", logDetails = "Created test case." @@ -84,23 +102,25 @@ internal class DataEntryRepositoryITest { } dataEntry2 = DataEntryEntity( - dataEntryId = DataEntryId(entryType = "test", entryId = id2), + dataEntryId = DataEntryId(entryType = "test", entryId = UUID.randomUUID().toString()), type = "Test Entry", name = "Test Case 2", applicationName = "my-application", - state = DataEntryStateEmbeddable(state), + state = DataEntryStateEmbeddable(state2), revision = 12L, description = "This is a second test case.", lastModifiedDate = Instant.now(), + payload = json2, + payloadAttributes = payloadAttributes2, authorizedPrincipals = setOf( - AuthorizationPrincipal.group("avengers"), - AuthorizationPrincipal.user("piggy"), + group("avengers").toString(), + user("piggy").toString(), ) ).apply { this.protocol = listOf( ProtocolElement( dataEntry = this, - state = DataEntryStateEmbeddable(state), + state = DataEntryStateEmbeddable(state1), username = "ironman", logMessage = "Created other", logDetails = "Created test case 2." @@ -117,7 +137,6 @@ internal class DataEntryRepositoryITest { @After fun `remove principals`() { dataEntryRepository.deleteAll() - authorizationPrincipalRepository.deleteAll() entityManager.flush() } @@ -130,22 +149,22 @@ internal class DataEntryRepositoryITest { @Test fun `should find data entries by authorization`() { - val muppets = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.group("muppets"))) + val muppets = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(group("muppets").toString())) assertThat(muppets).containsExactly(dataEntry) - val kermit = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.user("kermit"))) + val kermit = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(user("kermit").toString())) assertThat(kermit).containsExactly(dataEntry) - val piggy = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.user("piggy"))) + val piggy = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(user("piggy").toString())) assertThat(piggy).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) - val avengers = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.group("avengers"))) + val avengers = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(group("avengers").toString())) assertThat(avengers).containsExactly(dataEntry2) - val unknownGroup = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.group("unknown group"))) + val unknownGroup = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(group("unknown group").toString())) assertThat(unknownGroup).isEmpty() - val unknownUser = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(AuthorizationPrincipal.user("unknown user"))) + val unknownUser = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(user("unknown user").toString())) assertThat(unknownUser).isEmpty() } @@ -155,5 +174,36 @@ internal class DataEntryRepositoryITest { assertThat(all).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) } + @Test + fun `should find by filter`() { + val byStateInProgress = dataEntryRepository.findAll(where(hasState("In progress"))) + assertThat(byStateInProgress).containsExactlyInAnyOrderElementsOf(listOf(dataEntry)) + + val byStateInReview = dataEntryRepository.findAll(where(hasState("In review"))) + assertThat(byStateInReview).containsExactlyInAnyOrderElementsOf(listOf(dataEntry2)) + + val byProcessingTypeInProgress = dataEntryRepository.findAll(where(hasProcessingType(ProcessingType.IN_PROGRESS))) + assertThat(byProcessingTypeInProgress).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) + + val byProcessingTypeCompleted = dataEntryRepository.findAll(where(hasProcessingType(ProcessingType.COMPLETED))) + assertThat(byProcessingTypeCompleted).isEmpty() + + val byPayloadFilterByChildKeyNumberValue = dataEntryRepository.findAll(where(hasPayloadAttribute("child.key-number", "42"))) + assertThat(byPayloadFilterByChildKeyNumberValue).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) + + val byPayloadFilterByChildKeyValue = + dataEntryRepository.findAll( + where(hasPayloadAttribute("child.key", "value")) + .and(hasPayloadAttribute("id", dataEntry.dataEntryId.entryId)) + ) + assertThat(byPayloadFilterByChildKeyValue).containsExactlyInAnyOrderElementsOf(listOf(dataEntry)) + + val piggy = dataEntryRepository.findAll(isAuthorizedFor(setOf(user("piggy")))) + assertThat(piggy).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) + + val kermitOrAvengers = dataEntryRepository.findAll(isAuthorizedFor(setOf(user("kermit"), group("avengers")))) + assertThat(kermitOrAvengers).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) + + } } diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/SpecificationTest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/SpecificationTest.kt new file mode 100644 index 000000000..f41c8b545 --- /dev/null +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/SpecificationTest.kt @@ -0,0 +1,43 @@ +package io.holunda.polyflow.view.jpa.data + +import io.holunda.polyflow.view.filter.toCriteria +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.group +import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.user +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +/** + * Tests conversion of criteria into JPA Specifications. + */ +internal class SpecificationTest { + + @Test + internal fun `should create single attribute specification`() { + val filters = listOf("data.state.state=In Progress") + val criteria = toCriteria(filters) + + val specification = criteria.toSpecification() + assertThat(specification).isNotNull + } + + @Test + internal fun `should create multiple attribute specification`() { + val filters = listOf("data.state.state=In Progress", "data.state.processingType=IN_PROGRESS") + val criteria = toCriteria(filters) + + val specification = criteria.toSpecification() + assertThat(specification).isNotNull + } + + @Test + internal fun `should create multiple principal specifications`() { + val spec = DataEntryRepository.Companion.isAuthorizedFor(setOf( + user("kermit"), + group("muppets"), + group("avengers"), + )) + + assertThat(spec).isNotNull + } + +} diff --git a/view/jpa/src/test/resources/application-itest.yaml b/view/jpa/src/test/resources/application-itest.yaml index 71344dba7..0588e6877 100644 --- a/view/jpa/src/test/resources/application-itest.yaml +++ b/view/jpa/src/test/resources/application-itest.yaml @@ -1,12 +1,10 @@ spring: jpa: open-in-view: true # disable JPA warning - show-sql: true + show-sql: false + +# logger: +# level: +# org.hibernate.type: TRACE # activate this and generic ROOT logger to see SQL and binding -logger: - level: - org.hibernate.type: TRACE - org.hibernate.engine.internal.cascade: DEBUG - org.springframework: INFO - org.springframework.beans: INFO diff --git a/view/jpa/src/test/resources/banner.txt b/view/jpa/src/test/resources/banner.txt index fbed1d171..3c194d69e 100644 --- a/view/jpa/src/test/resources/banner.txt +++ b/view/jpa/src/test/resources/banner.txt @@ -1,3 +1,3 @@ -_______________________________________________________________________________ -JPA View I-Test -_______________________________________________________________________________ +=============================================================================== +JPA View I-Test, using SpringBoot ${spring-boot.formatted-version} +=============================================================================== diff --git a/view/jpa/src/test/resources/logback.xml b/view/jpa/src/test/resources/logback.xml index e32451612..c120a71dd 100644 --- a/view/jpa/src/test/resources/logback.xml +++ b/view/jpa/src/test/resources/logback.xml @@ -5,7 +5,8 @@ - + + diff --git a/view/pom.xml b/view/pom.xml index 67fb4763a..7380c777f 100755 --- a/view/pom.xml +++ b/view/pom.xml @@ -21,6 +21,7 @@ view-api + filtering simple jpa mongo diff --git a/view/simple/pom.xml b/view/simple/pom.xml index 99ece3e2c..09eed4f81 100755 --- a/view/simple/pom.xml +++ b/view/simple/pom.xml @@ -16,6 +16,10 @@ io.holunda.polyflow polyflow-view-api + + io.holunda.polyflow + polyflow-view-filtering + org.axonframework diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleDataEntryService.kt b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleDataEntryService.kt index 79502bc5d..6eed73732 100644 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleDataEntryService.kt +++ b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleDataEntryService.kt @@ -6,11 +6,11 @@ import io.holunda.camunda.taskpool.api.business.DataEntryCreatedEvent import io.holunda.camunda.taskpool.api.business.DataEntryUpdatedEvent import io.holunda.camunda.taskpool.api.business.dataIdentityString import io.holunda.polyflow.view.DataEntry +import io.holunda.polyflow.view.filter.createDataEntryPredicates +import io.holunda.polyflow.view.filter.filterByPredicate +import io.holunda.polyflow.view.filter.toCriteria import io.holunda.polyflow.view.query.data.* -import io.holunda.polyflow.view.simple.filter.createDataEntryPredicates -import io.holunda.polyflow.view.simple.filter.filterByPredicate -import io.holunda.polyflow.view.simple.filter.toCriteria -import io.holunda.polyflow.view.simple.sort.dataComparator +import io.holunda.polyflow.view.sort.dataComparator import mu.KLogging import org.axonframework.config.ProcessingGroup import org.axonframework.eventhandling.EventHandler diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleServiceViewProcessingGroup.kt b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleServiceViewProcessingGroup.kt index 8c8e2628f..938d9565e 100644 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleServiceViewProcessingGroup.kt +++ b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleServiceViewProcessingGroup.kt @@ -12,7 +12,7 @@ class SimpleServiceViewProcessingGroup( companion object : KLogging() { - const val PROCESSING_GROUP = "io.holunda.camunda.taskpool.view.simple.service" + const val PROCESSING_GROUP = "io.holunda.polyflow.view.simple" } /** diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleTaskPoolService.kt b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleTaskPoolService.kt index 81b093f06..39ec996be 100755 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleTaskPoolService.kt +++ b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/SimpleTaskPoolService.kt @@ -8,10 +8,10 @@ import io.holunda.polyflow.view.DataEntry import io.holunda.polyflow.view.Task import io.holunda.polyflow.view.TaskWithDataEntries import io.holunda.polyflow.view.query.task.* -import io.holunda.polyflow.view.simple.filter.createTaskPredicates -import io.holunda.polyflow.view.simple.filter.filterByPredicate -import io.holunda.polyflow.view.simple.filter.toCriteria -import io.holunda.polyflow.view.simple.sort.taskComparator +import io.holunda.polyflow.view.filter.createTaskPredicates +import io.holunda.polyflow.view.filter.filterByPredicate +import io.holunda.polyflow.view.filter.toCriteria +import io.holunda.polyflow.view.sort.taskComparator import io.holunda.polyflow.view.simple.updateMapFilterQuery import io.holunda.polyflow.view.task import mu.KLogging diff --git a/view/simple/src/test/kotlin/io/holunda/polyflow/view/TaskWithDataEntriesTest.kt b/view/simple/src/test/kotlin/io/holunda/polyflow/view/TaskWithDataEntriesCorrelationTest.kt similarity index 98% rename from view/simple/src/test/kotlin/io/holunda/polyflow/view/TaskWithDataEntriesTest.kt rename to view/simple/src/test/kotlin/io/holunda/polyflow/view/TaskWithDataEntriesCorrelationTest.kt index 832d447e9..222000619 100644 --- a/view/simple/src/test/kotlin/io/holunda/polyflow/view/TaskWithDataEntriesTest.kt +++ b/view/simple/src/test/kotlin/io/holunda/polyflow/view/TaskWithDataEntriesCorrelationTest.kt @@ -10,7 +10,7 @@ import org.camunda.bpm.engine.variable.Variables import org.junit.Test import java.util.* -class TaskWithDataEntriesTest { +class TaskWithDataEntriesCorrelationTest { private val dataEntry1 = DataEntry( entryType = "EntryType1", From dfa7cb0407c9feaf942476c7724c9ec4bf02434e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Aug 2021 16:59:03 +0000 Subject: [PATCH 15/27] chore(deps): bump reactor-kotlin-extensions from 1.1.3 to 1.1.4 Bumps [reactor-kotlin-extensions](https://github.com/reactor/reactor-kotlin-extensions) from 1.1.3 to 1.1.4. - [Release notes](https://github.com/reactor/reactor-kotlin-extensions/releases) - [Commits](https://github.com/reactor/reactor-kotlin-extensions/compare/v1.1.3...v1.1.4) --- updated-dependencies: - dependency-name: io.projectreactor.kotlin:reactor-kotlin-extensions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- view/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 view/pom.xml diff --git a/view/pom.xml b/view/pom.xml old mode 100755 new mode 100644 index 67fb4763a..099c5e793 --- a/view/pom.xml +++ b/view/pom.xml @@ -16,7 +16,7 @@ 4.4 - 1.1.3 + 1.1.4 From c941c20524ed3ca693a110eed1aa84ce95601cb3 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Thu, 12 Aug 2021 11:55:00 +0200 Subject: [PATCH 16/27] allow multiple data entries update with the same message without duplications, fix #406, implement data entry projection in JPA, fix #227 --- bom/parent/pom.xml | 2 +- bom/taskpool-dependencies/pom.xml | 5 ++ docs/examples/scenarios/index.md | 2 +- .../taskpool-application/pom.xml | 4 + .../process/view/jpa/JpaViewConfiguration.kt | 10 +++ .../view/simple/SimpleViewConfiguration.kt | 2 +- .../src/main/kotlin/VariableSerializer.kt | 11 +-- view/filtering/pom.xml | 5 -- view/jpa/pom.xml | 42 ++++++++++ .../view/jpa/EnablePolyflowJpaView.kt | 2 +- .../view/jpa/PolyflowJpaViewConfiguration.kt | 5 +- .../view/jpa/data/AuthorizationPrincipal.kt | 4 +- .../polyflow/view/jpa/data/Converters.kt | 51 ++++++------ .../polyflow/view/jpa/data/DataEntryEntity.kt | 6 +- .../polyflow/view/jpa/data/DataEntryId.kt | 4 +- .../view/jpa/data/DataEntryStateEmbeddable.kt | 4 +- .../polyflow/view/jpa/data/ProtocolElement.kt | 10 +-- .../jpa/data/AuthorizationPrincipalTest.kt | 9 ++- .../polyflow/view/jpa/data/DataEntryIdTest.kt | 8 +- .../view/jpa/data/DataEntryRepositoryITest.kt | 12 +-- .../resources/META-INF/persistence.xml | 0 .../holunda/polyflow/view/mongo/Converters.kt | 7 +- .../view/simple/service/Converters.kt | 6 +- view/view-api/src/main/kotlin/Extensions.kt | 14 +++- .../src/test/kotlin/ExtensionsTest.kt | 78 +++++++++++++++++++ 25 files changed, 231 insertions(+), 72 deletions(-) create mode 100644 examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/jpa/JpaViewConfiguration.kt rename view/jpa/src/{main => test}/resources/META-INF/persistence.xml (100%) create mode 100644 view/view-api/src/test/kotlin/ExtensionsTest.kt diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index 7501eb96a..3660cd2e7 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -632,7 +632,7 @@ - ${project.basedir}/src/main/kotlin + src/main/kotlin diff --git a/bom/taskpool-dependencies/pom.xml b/bom/taskpool-dependencies/pom.xml index 276bfce39..64134c4b9 100644 --- a/bom/taskpool-dependencies/pom.xml +++ b/bom/taskpool-dependencies/pom.xml @@ -64,6 +64,11 @@ polyflow-view-simple ${project.version} + + io.holunda.polyflow + polyflow-view-jpa + ${project.version} + io.holunda.polyflow polyflow-view-mongo diff --git a/docs/examples/scenarios/index.md b/docs/examples/scenarios/index.md index 4f828e17e..58b3bc7ba 100644 --- a/docs/examples/scenarios/index.md +++ b/docs/examples/scenarios/index.md @@ -12,7 +12,7 @@ events and queries and additional requirements of event-sourced persistence a sp command bus, event bus and event store is required. In particular, two scenarios can be distinguished: using Axon Server or using a different distribution technology. -The provided [Example application](../example-application/) is implemented several times demonstrating the following usage scenarios: +The provided [Example application](../example-approval) is implemented several times demonstrating the following usage scenarios: * [Single Node Scenario](single-node) * [Distributed Scenario using Axon Server](distributed-axon-server) diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml index c1e0c254c..0eed1a5f4 100755 --- a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml @@ -37,6 +37,10 @@ io.holunda.polyflow polyflow-view-simple + + io.holunda.polyflow + polyflow-view-jpa + io.holunda.polyflow polyflow-view-mongo diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/jpa/JpaViewConfiguration.kt b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/jpa/JpaViewConfiguration.kt new file mode 100644 index 000000000..15665f7f6 --- /dev/null +++ b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/jpa/JpaViewConfiguration.kt @@ -0,0 +1,10 @@ +package io.holunda.camunda.taskpool.example.process.view.jpa + +import io.holunda.polyflow.view.jpa.EnablePolyflowJpaView +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Profile + +@Configuration +@Profile("jpa") +@EnablePolyflowJpaView +class JpaViewConfiguration diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt index fe2bca40c..a10256054 100644 --- a/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt +++ b/examples/scenarios/distributed-axon-server/taskpool-application/src/main/kotlin/io/holunda/camunda/taskpool/example/process/view/simple/SimpleViewConfiguration.kt @@ -5,6 +5,6 @@ import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Profile @Configuration -@Profile("!mongo") +@Profile("simple") @EnablePolyflowMongoView class SimpleViewConfiguration diff --git a/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt b/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt index ec0f5d6ef..675b50480 100644 --- a/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt +++ b/integration/common/variable-serializer/src/main/kotlin/VariableSerializer.kt @@ -48,7 +48,7 @@ fun VariableMap.toJsonPathsWithValues(limit: Int = -1): Map { } -internal fun MutableMap.MutableEntry.toJsonPathWithValue(prefix: String = "", limit: Int = -1): List> { +internal fun MutableMap.MutableEntry.toJsonPathWithValue(prefix: String = "", limit: Int = -1): List> { // level limit check val currentLevel = prefix.count { ".".contains(it) } if (limit != -1 && currentLevel >= limit) { @@ -60,11 +60,12 @@ internal fun MutableMap.MutableEntry.toJsonPathWithValue(prefix: St } else { "$prefix.${this.key}" } - return if (this.value.isPrimitiveType()) { - listOf(key to this.value) - } else if (this.value is Map<*, *>) { + val value = this.value + return if (value != null && value.isPrimitiveType()) { + listOf(key to value) + } else if (value is Map<*, *>) { @Suppress("UNCHECKED_CAST") - (this.value as Map).toMutableMap().entries.map { it.toJsonPathWithValue(key, limit) }.flatten() + (value as Map).toMutableMap().entries.map { it.toJsonPathWithValue(key, limit) }.flatten() } else { // ignore complex objects listOf() diff --git a/view/filtering/pom.xml b/view/filtering/pom.xml index 7231100e4..decb8376f 100644 --- a/view/filtering/pom.xml +++ b/view/filtering/pom.xml @@ -19,11 +19,6 @@ ${project.version} - - org.apache.commons - commons-text - 1.9 - com.fasterxml.jackson.core jackson-core diff --git a/view/jpa/pom.xml b/view/jpa/pom.xml index 6f9b364d5..1923755fb 100755 --- a/view/jpa/pom.xml +++ b/view/jpa/pom.xml @@ -141,6 +141,38 @@ h2_ddl.sql + + generate-mssql-ddl + + create + + + org.hibernate.dialect.SQLServer2012Dialect + + org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + + false + true + true + mssql_ddl.sql + + + + generate-posgresql-ddl + + create + + + org.hibernate.dialect.PostgreSQL10Dialect + + org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + + false + true + true + pgsql_ddl.sql + + @@ -148,6 +180,16 @@ jaxb-api 2.4.0-b180830.0359 + + com.microsoft.sqlserver + mssql-jdbc + 9.2.1.jre11 + + + org.postgresql + postgresql + 42.2.19 + diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt index 39c102d86..2b3e28653 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/EnablePolyflowJpaView.kt @@ -6,5 +6,5 @@ import org.springframework.context.annotation.Import * Enables polyflow projection using RDMBS via JPA as persistence. */ @MustBeDocumented -@Import(io.holunda.polyflow.view.jpa.PolyflowJpaViewConfiguration::class) +@Import(PolyflowJpaViewConfiguration::class) annotation class EnablePolyflowJpaView diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt index 2d0caeb43..42db13c74 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt @@ -1,10 +1,11 @@ package io.holunda.polyflow.view.jpa -import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.config.EnableJpaRepositories @Configuration -@EntityScan @EnableConfigurationProperties(PolyflowJpaViewProperties::class) +@ComponentScan class PolyflowJpaViewConfiguration diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt index 396591aa2..a4b46a8ba 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt @@ -9,8 +9,8 @@ data class AuthorizationPrincipal( ) { companion object { operator fun invoke(auth: String) = auth.split(":").let { - require(it.size == 2) { "Illegal auth format, expecting :" } - AuthorizationPrincipal(type = AuthorizationPrincipalType.valueOf(it[0]), name = it[1]) + require(it.size >= 2) { "Illegal auth format, expecting :, received '$auth'" } + AuthorizationPrincipal(type = AuthorizationPrincipalType.valueOf(it[0]), name = it.subList(1, it.size).joinToString(":")) } fun group(name: String): AuthorizationPrincipal = AuthorizationPrincipal(name = name, type = AuthorizationPrincipalType.GROUP) diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt index efaf211f5..36c8a58f1 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt @@ -8,6 +8,7 @@ import io.holunda.camunda.variable.serializer.toPayloadJson import io.holunda.camunda.variable.serializer.toPayloadVariableMap import io.holunda.polyflow.view.DataEntry import io.holunda.polyflow.view.ProtocolEntry +import io.holunda.polyflow.view.addModification import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.group import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.user import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipalType.GROUP @@ -70,7 +71,7 @@ fun DataEntryState.toState() = DataEntryStateEmbeddable(processingType = this.pr fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue, limit: Int) = DataEntryEntity( dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), payload = this.payload.toPayloadJson(objectMapper), - payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toSet(), + payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toMutableSet(), name = this.name, applicationName = this.applicationName, type = this.type, @@ -84,10 +85,14 @@ fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } else { 0L }, - authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { user(it).toString() } - .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { group(it).toString() }).toSet(), + authorizedPrincipals = AuthorizationChange.applyUserAuthorization(mutableSetOf(), this.authorizations).map { user(it).toString() } + .plus(AuthorizationChange.applyGroupAuthorization(mutableSetOf(), this.authorizations).map { group(it).toString() }).toMutableSet(), ).apply { - this.protocol = this.protocol.addModification(this, this@toEntity.createModification, this@toEntity.state) + this.protocol = this.protocol + .map { it.toProtocolEntry() } + .addModification(this@toEntity.createModification, this@toEntity.state) + .map { it.toEntity(this) } + .toMutableList() } /** @@ -97,7 +102,7 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re DataEntryEntity( dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), payload = this.payload.toPayloadJson(objectMapper), - payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toSet(), + payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toMutableSet(), name = this.name, applicationName = this.applicationName, type = this.type, @@ -107,7 +112,7 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re state = this.state.toState(), formKey = this.formKey, authorizedPrincipals = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations).map { user(it).toString() } - .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { group(it).toString() }).toSet(), + .plus(AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations).map { group(it).toString() }).toMutableSet(), revision = if (revisionValue != RevisionValue.NO_REVISION) { revisionValue.revision } else { @@ -117,7 +122,7 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } else { oldEntry.also { it.payload = this.payload.toPayloadJson(objectMapper) - it.payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toSet() + it.payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toMutableSet() it.name = this.name it.applicationName = this.applicationName it.type = this.type @@ -134,7 +139,7 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re it.authorizedPrincipals.asGroupnames(), this.authorizations ).map { group(it).toString() }) - .toSet() + .toMutableSet() it.revision = if (revisionValue != RevisionValue.NO_REVISION) { revisionValue.revision } else { @@ -142,25 +147,23 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } } }.apply { - this.protocol = this.protocol.addModification(this, this@toEntity.updateModification, this@toEntity.state) + this.protocol = this.protocol + .map { it.toProtocolEntry() } + .addModification(this@toEntity.updateModification, this@toEntity.state) + .map { it.toEntity(this) } + .toMutableList() } /** - * Adds a modification to the protocol, if it doesn't exist in the protocol already, comparing all protocol element properties - * besides the technical id. + * Converts a protocol entry to entity for given data entry entity. */ -fun List.addModification(dataEntry: DataEntryEntity, modification: Modification, state: DataEntryState) = - ProtocolElement( +fun ProtocolEntry.toEntity(dataEntry: DataEntryEntity): ProtocolElement { + return ProtocolElement( dataEntry = dataEntry, - time = modification.time.toInstant(), - username = modification.username, - logMessage = modification.log, - logDetails = modification.logNotes, + time = this.time, + username = this.username, + logMessage = this.logMessage, + logDetails = this.logDetails, state = state.toState() - ).let { protocolElement -> - if (dataEntry.protocol.any { existing -> existing.same(protocolElement) }) { - this - } else { - this.plus(protocolElement) - } - } + ) +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt index 82bb45fca..349ddc678 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt @@ -42,7 +42,7 @@ class DataEntryEntity( ] ) @Column(name = "AUTHORIZED_PRINCIPAL", nullable = false) - var authorizedPrincipals: Set = setOf(), + var authorizedPrincipals: MutableSet = mutableSetOf(), @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( @@ -53,10 +53,10 @@ class DataEntryEntity( ] ) @Column(name = "PAYLOAD_ATTRIBUTE", nullable = false) - var payloadAttributes: Set = setOf(), + var payloadAttributes: MutableSet = mutableSetOf(), @OneToMany(mappedBy = "dataEntry", orphanRemoval = true, cascade = [CascadeType.ALL], targetEntity = ProtocolElement::class, fetch = FetchType.EAGER) - var protocol: List = mutableListOf(), + var protocol: MutableList = mutableListOf(), @Lob var payload: String? = null, diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt index 612397b49..8d0a2cd2b 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt @@ -15,8 +15,8 @@ class DataEntryId( companion object { operator fun invoke(identity: String): DataEntryId = identity.split(":").let { - require(it.size == 2) { "Illegal identity format, expecting :" } - DataEntryId(entryType = it[0], entryId = it[1]) + require(it.size >= 2) { "Illegal identity format, expecting :, received '$identity'" } + DataEntryId(entryType = it[0], entryId = it.subList(1, it.size).joinToString(":")) } } diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt index c402dd049..f86c1761f 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryStateEmbeddable.kt @@ -8,9 +8,9 @@ import javax.persistence.Embeddable @Embeddable class DataEntryStateEmbeddable( @Column(name = "PROCESSING_TYPE", nullable = false) - val processingType: String, + var processingType: String, @Column(name = "STATE", nullable = false) - val state: String + var state: String ) : Serializable { companion object { operator fun invoke(state: DataEntryState): DataEntryStateEmbeddable = diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt index ad5d93159..ec18b8277 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt @@ -11,15 +11,15 @@ class ProtocolElement( var id: String = UUID.randomUUID().toString(), @Column(name = "TIME", nullable = false) - val time: Instant = Instant.now(), + var time: Instant = Instant.now(), @Embedded - val state: DataEntryStateEmbeddable, + var state: DataEntryStateEmbeddable, @Column(name = "USERNAME", nullable = true) - val username: String? = null, + var username: String? = null, @Column(name = "LOG_MESSAGE", nullable = true) - val logMessage: String? = null, + var logMessage: String? = null, @Column(name = "LOG_DETAILS", nullable = true) - val logDetails: String? = null, + var logDetails: String? = null, @ManyToOne @JoinColumns( diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt index f2dc239ac..15e8bfc02 100644 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipalTest.kt @@ -14,6 +14,13 @@ internal class AuthorizationPrincipalTest { assertThat(id).isEqualTo(group(name = "groupName")) } + @Test + fun `should construct authorization principal id for group created from a client role`() { + val id = AuthorizationPrincipal("GROUP:client-id:groupName") + assertThat(id).isEqualTo(group(name = "client-id:groupName")) + } + + @Test fun `should construct authorization principal id for user`() { val id = AuthorizationPrincipal("USER:userName") @@ -22,7 +29,7 @@ internal class AuthorizationPrincipalTest { @Test fun `should not construct authorization principal id`() { - assertThatThrownBy { AuthorizationPrincipal("bad string") }.hasMessage("Illegal auth format, expecting :") + assertThatThrownBy { AuthorizationPrincipal("bad string") }.hasMessage("Illegal auth format, expecting :, received 'bad string'") } diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt index 159276cd0..e615359b8 100644 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryIdTest.kt @@ -12,10 +12,16 @@ internal class DataEntryIdTest { assertThat(id).isEqualTo(DataEntryId(entryType = "type", entryId = "id")) } + @Test + fun `should construct data entry id with id containing a colon`() { + val id = DataEntryId("type:id:k") + assertThat(id).isEqualTo(DataEntryId(entryType = "type", entryId = "id:k")) + } + @Test fun `should not construct data entry id`() { - assertThatThrownBy { DataEntryId("bad string") }.hasMessage("Illegal identity format, expecting :") + assertThatThrownBy { DataEntryId("bad string") }.hasMessage("Illegal identity format, expecting :, received 'bad string'") } diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt index b1772653f..2d06dd5fa 100644 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt @@ -82,15 +82,15 @@ internal class DataEntryRepositoryITest { description = "This is a test case.", revision = 1L, lastModifiedDate = Instant.now(), - authorizedPrincipals = setOf( + authorizedPrincipals = mutableSetOf( group("muppets").toString(), user("kermit").toString(), user("piggy").toString(), ), payload = json, - payloadAttributes = payloadAttributes + payloadAttributes = payloadAttributes.toMutableSet() ).apply { - this.protocol = listOf( + this.protocol = mutableListOf( ProtocolElement( dataEntry = this, state = DataEntryStateEmbeddable(state1), @@ -111,13 +111,13 @@ internal class DataEntryRepositoryITest { description = "This is a second test case.", lastModifiedDate = Instant.now(), payload = json2, - payloadAttributes = payloadAttributes2, - authorizedPrincipals = setOf( + payloadAttributes = payloadAttributes2.toMutableSet(), + authorizedPrincipals = mutableSetOf( group("avengers").toString(), user("piggy").toString(), ) ).apply { - this.protocol = listOf( + this.protocol = mutableListOf( ProtocolElement( dataEntry = this, state = DataEntryStateEmbeddable(state1), diff --git a/view/jpa/src/main/resources/META-INF/persistence.xml b/view/jpa/src/test/resources/META-INF/persistence.xml similarity index 100% rename from view/jpa/src/main/resources/META-INF/persistence.xml rename to view/jpa/src/test/resources/META-INF/persistence.xml diff --git a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/Converters.kt b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/Converters.kt index 25000d9e6..c035d2903 100644 --- a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/Converters.kt +++ b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/Converters.kt @@ -4,6 +4,7 @@ import io.holunda.camunda.taskpool.api.business.AuthorizationChange import io.holunda.camunda.taskpool.api.business.DataEntryCreatedEvent import io.holunda.camunda.taskpool.api.business.DataEntryUpdatedEvent import io.holunda.camunda.taskpool.api.business.dataIdentityString +import io.holunda.polyflow.view.ProtocolEntry import io.holunda.polyflow.view.Task import io.holunda.polyflow.view.TaskWithDataEntries import io.holunda.polyflow.view.addModification @@ -28,7 +29,7 @@ fun DataEntryCreatedEvent.toDocument() = DataEntryDocument( type = this.type, authorizedUsers = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations), authorizedGroups = AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations), - protocol = addModification(listOf(), this.createModification, this.state).map { it.toProtocolElement() }, + protocol = listOf().addModification(this.createModification, this.state).map { it.toProtocolElement() }, createdDate = this.createModification.time.toInstant(), lastModifiedDate = this.createModification.time.toInstant(), applicationName = this.applicationName, @@ -52,7 +53,7 @@ fun DataEntryUpdatedEvent.toDocument(oldDocument: DataEntryDocument?) = if (oldD authorizedUsers = authorizedUsers, authorizedGroups = authorizedGroups, authorizedPrincipals = DataEntryDocument.authorizedPrincipals(authorizedUsers, authorizedGroups), - protocol = addModification(oldDocument.protocol.map { it.toProtocol() }, this.updateModification, this.state).map { it.toProtocolElement() }, + protocol = oldDocument.protocol.map { it.toProtocol() }.addModification(this.updateModification, this.state).map { it.toProtocolElement() }, lastModifiedDate = this.updateModification.time.toInstant(), applicationName = this.applicationName, state = this.state.state, @@ -70,7 +71,7 @@ fun DataEntryUpdatedEvent.toDocument(oldDocument: DataEntryDocument?) = if (oldD type = this.type, authorizedUsers = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations), authorizedGroups = AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations), - protocol = addModification(listOf(), this.updateModification, this.state).map { it.toProtocolElement() }, + protocol = listOf().addModification(this.updateModification, this.state).map { it.toProtocolElement() }, createdDate = this.updateModification.time.toInstant(), lastModifiedDate = this.updateModification.time.toInstant(), applicationName = this.applicationName, diff --git a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/Converters.kt b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/Converters.kt index d372422b1..ac619c47b 100644 --- a/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/Converters.kt +++ b/view/simple/src/main/kotlin/io/holunda/polyflow/view/simple/service/Converters.kt @@ -29,7 +29,7 @@ fun DataEntryUpdatedEvent.toDataEntry(oldEntry: DataEntry?) = if (oldEntry == nu formKey = this.formKey, authorizedUsers = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations), authorizedGroups = AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations), - protocol = addModification(listOf(), this.updateModification, this.state) + protocol = listOf().addModification(this.updateModification, this.state) ) } else { oldEntry.copy( @@ -43,7 +43,7 @@ fun DataEntryUpdatedEvent.toDataEntry(oldEntry: DataEntry?) = if (oldEntry == nu formKey = this.formKey, authorizedUsers = AuthorizationChange.applyUserAuthorization(oldEntry.authorizedUsers, this.authorizations), authorizedGroups = AuthorizationChange.applyGroupAuthorization(oldEntry.authorizedGroups, this.authorizations), - protocol = addModification(oldEntry.protocol, this.updateModification, this.state) + protocol = oldEntry.protocol.addModification(this.updateModification, this.state) ) } @@ -63,7 +63,7 @@ fun DataEntryCreatedEvent.toDataEntry() = DataEntry( formKey = this.formKey, authorizedUsers = AuthorizationChange.applyUserAuthorization(setOf(), this.authorizations), authorizedGroups = AuthorizationChange.applyGroupAuthorization(setOf(), this.authorizations), - protocol = addModification(listOf(), this.createModification, this.state) + protocol = listOf().addModification(this.createModification, this.state) ) /** diff --git a/view/view-api/src/main/kotlin/Extensions.kt b/view/view-api/src/main/kotlin/Extensions.kt index e05cdaf59..8c4aa4cdc 100644 --- a/view/view-api/src/main/kotlin/Extensions.kt +++ b/view/view-api/src/main/kotlin/Extensions.kt @@ -3,7 +3,6 @@ package io.holunda.polyflow.view import io.holunda.camunda.taskpool.api.business.DataEntryState import io.holunda.camunda.taskpool.api.business.Modification import io.holunda.camunda.taskpool.api.task.* -import java.util.* /** * Create a new task from data of existing task and incoming event. @@ -166,12 +165,19 @@ fun task(event: TaskCandidateUserChanged, task: Task) = Task( /** * Adds modification to the list of protocol entry list. + * Make sure there are no duplicates in this list. */ -fun addModification(modifications: List, modification: Modification, state: DataEntryState) = - modifications.plus(ProtocolEntry( +fun List.addModification(modification: Modification, state: DataEntryState) = + ProtocolEntry( time = modification.time.toInstant(), username = modification.username, logMessage = modification.log, logDetails = modification.logNotes, state = state - )) + ).let { protocolEntry -> + if (this.any { existing -> existing == protocolEntry }) { + this + } else { + this.plus(protocolEntry) + } + } diff --git a/view/view-api/src/test/kotlin/ExtensionsTest.kt b/view/view-api/src/test/kotlin/ExtensionsTest.kt new file mode 100644 index 000000000..d22bc6a24 --- /dev/null +++ b/view/view-api/src/test/kotlin/ExtensionsTest.kt @@ -0,0 +1,78 @@ +package io.holunda.polyflow.view + +import io.holunda.camunda.taskpool.api.business.Modification +import io.holunda.camunda.taskpool.api.business.ProcessingType +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import java.time.Instant +import java.time.ZoneOffset + +internal class ExtensionsTest { + + private val now = Instant.now() + private val entry1 = ProtocolEntry( + time = now, + state = ProcessingType.IN_PROGRESS.of("Created"), + username = "kermit", + logMessage = "Created entry", + logDetails = "Successfully created an entry" + ) + private val entry2 = ProtocolEntry( + time = now.plusSeconds(10), + state = ProcessingType.IN_PROGRESS.of("Updated"), + username = "kermit", + logMessage = "Updated entry", + logDetails = null + ) + + @Test + fun `should not add protocol entry if already in the list`() { + + val protocol = listOf( + entry1, entry2 + ) + + val result = protocol.addModification( + modification = Modification( + time = entry1.time.atOffset(ZoneOffset.UTC), + username = entry1.username, + log = entry1.logMessage, + logNotes = entry1.logDetails + ), + state = entry1.state + ) + + assertThat(result).isEqualTo(protocol) + } + + @Test + fun `should add protocol entry if not already in the list`() { + + val protocol = listOf( + entry1, entry2 + ) + + val result = protocol.addModification( + modification = Modification( + time = now.plusSeconds(25).atOffset(ZoneOffset.UTC), + username = entry1.username, + log = entry1.logMessage, + logNotes = entry1.logDetails + ), + state = entry1.state + ) + + assertThat(result).isNotEqualTo(protocol) + assertThat(result.size).isEqualTo(protocol.size + 1) + assertThat(result).containsAll(protocol) + assertThat(result).contains( + ProtocolEntry( + time = now.plusSeconds(25), + state = entry1.state, + username = entry1.username, + logMessage = entry1.logMessage, + logDetails = entry1.logDetails + ) + ) + } +} From 7c964dd25cdf648c2e332d7a95113e40e282c7cf Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Thu, 12 Aug 2021 13:16:55 +0200 Subject: [PATCH 17/27] fix tests for protocol entries --- .../polyflow/view/jpa/data/Converters.kt | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt index 36c8a58f1..f8c9f8536 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt @@ -8,7 +8,6 @@ import io.holunda.camunda.variable.serializer.toPayloadJson import io.holunda.camunda.variable.serializer.toPayloadVariableMap import io.holunda.polyflow.view.DataEntry import io.holunda.polyflow.view.ProtocolEntry -import io.holunda.polyflow.view.addModification import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.group import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipal.Companion.user import io.holunda.polyflow.view.jpa.data.AuthorizationPrincipalType.GROUP @@ -88,11 +87,7 @@ fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re authorizedPrincipals = AuthorizationChange.applyUserAuthorization(mutableSetOf(), this.authorizations).map { user(it).toString() } .plus(AuthorizationChange.applyGroupAuthorization(mutableSetOf(), this.authorizations).map { group(it).toString() }).toMutableSet(), ).apply { - this.protocol = this.protocol - .map { it.toProtocolEntry() } - .addModification(this@toEntity.createModification, this@toEntity.state) - .map { it.toEntity(this) } - .toMutableList() + this.protocol = this.protocol.addModification(this, this@toEntity.createModification, this@toEntity.state).toMutableList() } /** @@ -147,23 +142,25 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } } }.apply { - this.protocol = this.protocol - .map { it.toProtocolEntry() } - .addModification(this@toEntity.updateModification, this@toEntity.state) - .map { it.toEntity(this) } - .toMutableList() + this.protocol = this.protocol.addModification(this, this@toEntity.updateModification, this@toEntity.state).toMutableList() } /** - * Converts a protocol entry to entity for given data entry entity. + * Adds a modification to the protocol, if it doesn't exist in the protocol already, comparing all protocol element properties + * besides the technical id. */ -fun ProtocolEntry.toEntity(dataEntry: DataEntryEntity): ProtocolElement { - return ProtocolElement( +fun List.addModification(dataEntry: DataEntryEntity, modification: Modification, state: DataEntryState) = + ProtocolElement( dataEntry = dataEntry, - time = this.time, - username = this.username, - logMessage = this.logMessage, - logDetails = this.logDetails, + time = modification.time.toInstant(), + username = modification.username, + logMessage = modification.log, + logDetails = modification.logNotes, state = state.toState() - ) -} + ).let { protocolElement -> + if (dataEntry.protocol.any { existing -> existing.same(protocolElement) }) { + this + } else { + this.plus(protocolElement) + } + } From f5c1fe3eb286bed32c12e74291430d2dc7502974 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Thu, 12 Aug 2021 14:04:20 +0200 Subject: [PATCH 18/27] remove duplications, fix codacy issues --- .../ProcessDefinitionEventUpcasters.kt | 7 +- .../enricher/ProcessVariablesCorrelator.kt | 6 ++ .../io/holunda/polyflow/view/filter/Filter.kt | 17 +++- .../view/jpa/PolyflowJpaViewProperties.kt | 3 + .../view/jpa/data/AuthorizationPrincipal.kt | 9 ++ .../polyflow/view/jpa/data/DataEntryId.kt | 3 + .../view/jpa/data/DataEntryRepository.kt | 3 + view/mongo/pom.xml | 4 + .../polyflow/view/mongo/MongoViewService.kt | 28 ++++++ .../polyflow/view/mongo/task/Criteria.kt | 99 ------------------- .../TaskWithDataEntriesRepositoryExtension.kt | 1 + ...kWithDataEntriesRepositoryExtensionImpl.kt | 4 + .../polyflow/view/mongo/filter/FilterTest.kt | 63 ------------ 13 files changed, 81 insertions(+), 166 deletions(-) delete mode 100755 view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/Criteria.kt delete mode 100755 view/mongo/src/test/kotlin/io/holunda/polyflow/view/mongo/filter/FilterTest.kt diff --git a/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt b/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt index 63dfc3c09..ad9cabc2f 100644 --- a/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt +++ b/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt @@ -7,7 +7,9 @@ import org.axonframework.serialization.upcasting.event.IntermediateEventRepresen import org.dom4j.Document import java.util.function.Function - +/** + * Upcaster to put revision 1 into the event and remove uneeded attributes. + */ @AnnotatedEventUpcaster("io.holunda.camunda.taskpool.api.task.ProcessDefinitionRegisteredEvent") class ProcessDefinitionEventXMLNullTo1Upcaster : AnnotationBasedSingleEventUpcaster() { @@ -35,6 +37,9 @@ class ProcessDefinitionEventXMLNullTo1Upcaster : AnnotationBasedSingleEventUpcas } } +/** + * Upcaster to put revision 1 into the event and remove uneeded attributes. + */ @AnnotatedEventUpcaster("io.holunda.camunda.taskpool.api.task.ProcessDefinitionRegisteredEvent", representationContentType = JSON) class ProcessDefinitionEventJSONNullTo1Upcaster : AnnotationBasedSingleEventUpcaster() { diff --git a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/camunda/taskpool/collector/task/enricher/ProcessVariablesCorrelator.kt b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/camunda/taskpool/collector/task/enricher/ProcessVariablesCorrelator.kt index 2262b29a4..77daa2acb 100755 --- a/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/camunda/taskpool/collector/task/enricher/ProcessVariablesCorrelator.kt +++ b/integration/camunda-bpm/taskpool-collector/src/main/kotlin/io/holunda/camunda/taskpool/collector/task/enricher/ProcessVariablesCorrelator.kt @@ -5,10 +5,16 @@ import io.holunda.camunda.taskpool.api.business.addCorrelation import io.holunda.camunda.taskpool.api.business.newCorrelations import org.camunda.bpm.engine.variable.VariableMap +/** + * Correlator for process variables. + */ class ProcessVariablesCorrelator(vararg correlations: ProcessVariableCorrelation) { private val all: Map = correlations.associate { it.processDefinitionKey to it } + /** + * Correlates variables from a given correlation map for a provided process definition and task definition. + */ fun correlateVariables(processDefinitionKey: ProcessDefinitionKey, taskDefinitionKey: TaskDefinitionKey, variables: VariableMap): CorrelationMap { val result = newCorrelations() diff --git a/view/filtering/src/main/kotlin/io/holunda/polyflow/view/filter/Filter.kt b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/filter/Filter.kt index 62483fb6d..66c2f12b3 100755 --- a/view/filtering/src/main/kotlin/io/holunda/polyflow/view/filter/Filter.kt +++ b/view/filtering/src/main/kotlin/io/holunda/polyflow/view/filter/Filter.kt @@ -156,7 +156,7 @@ fun List.toClassAttributePredicates(clazz: KClass) = .toList() /** - * Create critera for a map. + * Create criteria for a map. */ fun List.toPayloadPredicates() = this .asSequence() @@ -233,6 +233,17 @@ internal fun isDataEntryAttribute(propertyName: String): Boolean = * Criterion. */ sealed class Criterion(open val name: String, open val value: String, open val operator: String) { + + /** + * Value converter for criteria. + */ + fun typedValue(): Any = + when (this.name) { + "priority" -> this.value.toInt() + "createTime", "dueDate", "followUpDate" -> Instant.parse(this.value) + else -> this.value + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Criterion) return false @@ -279,7 +290,7 @@ sealed class Criterion(open val name: String, open val value: String, open val o * Wrapper for a pair of task and data entry predicate. */ data class TaskPredicateWrapper(val taskAttributePredicate: Predicate?, val taskPayloadPredicate: Predicate?) { - /* + /** * Checks if a payload predicate is set. */ fun hasPayloadPredicate() = taskPayloadPredicate != null @@ -294,7 +305,7 @@ data class TaskPredicateWrapper(val taskAttributePredicate: Predicate?, val * Wrapper for a pair of data entry and data payload predicate. */ data class DataEntryPredicateWrapper(val dataEntryAttributePredicate: Predicate?, val dataEntryPayloadPredicate: Predicate?) { - /* + /** * Checks if a payload predicate is set. */ fun hasPayloadPredicate() = dataEntryPayloadPredicate != null diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt index 34aa603c0..41848d7cf 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewProperties.kt @@ -3,6 +3,9 @@ package io.holunda.polyflow.view.jpa import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.ConstructorBinding +/** + * Properties to configure JPA View. + */ @ConstructorBinding @ConfigurationProperties(prefix = "polyflow.view.jpa") data class PolyflowJpaViewProperties( diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt index a4b46a8ba..fe3d62f56 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/AuthorizationPrincipal.kt @@ -8,12 +8,21 @@ data class AuthorizationPrincipal( val type: AuthorizationPrincipalType ) { companion object { + /** + * Factory method to construct principal out of string. + */ operator fun invoke(auth: String) = auth.split(":").let { require(it.size >= 2) { "Illegal auth format, expecting :, received '$auth'" } AuthorizationPrincipal(type = AuthorizationPrincipalType.valueOf(it[0]), name = it.subList(1, it.size).joinToString(":")) } + /** + * Constructor for the group principal. + */ fun group(name: String): AuthorizationPrincipal = AuthorizationPrincipal(name = name, type = AuthorizationPrincipalType.GROUP) + /** + * Constructor for the user principal. + */ fun user(name: String): AuthorizationPrincipal = AuthorizationPrincipal(name = name, type = AuthorizationPrincipalType.USER) } diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt index 8d0a2cd2b..05ce3809c 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt @@ -5,6 +5,9 @@ import java.util.* import javax.persistence.Column import javax.persistence.Embeddable +/** + * JPA composite id used for data entries. + */ @Embeddable class DataEntryId( @Column(name = "ENTRY_ID", nullable = false) diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt index f5dd0149a..44ab413e9 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt @@ -7,6 +7,9 @@ import org.springframework.data.repository.CrudRepository interface DataEntryRepository : CrudRepository, JpaSpecificationExecutor { + /** + * Finds all data entries with provided authorizations. + */ fun findAllByAuthorizedPrincipalsIn(authorizedPrincipalIds: Collection): List companion object { diff --git a/view/mongo/pom.xml b/view/mongo/pom.xml index 836586679..1953ee45f 100755 --- a/view/mongo/pom.xml +++ b/view/mongo/pom.xml @@ -17,6 +17,10 @@ io.holunda.polyflow polyflow-view-api + + io.holunda.polyflow + polyflow-view-filtering + org.springframework.boot diff --git a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt index 584c36be3..15da49c36 100755 --- a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt +++ b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/MongoViewService.kt @@ -7,6 +7,7 @@ import io.holunda.camunda.taskpool.api.business.dataIdentityString import io.holunda.camunda.taskpool.api.task.* import io.holunda.polyflow.view.Task import io.holunda.polyflow.view.TaskWithDataEntries +import io.holunda.polyflow.view.filter.toCriteria import io.holunda.polyflow.view.mongo.data.DataEntryChangeTracker import io.holunda.polyflow.view.mongo.data.DataEntryRepository import io.holunda.polyflow.view.mongo.data.dataEntry @@ -204,6 +205,9 @@ class MongoViewService( override fun query(query: TaskCountByApplicationQuery): CompletableFuture> = taskRepository.findTaskCountsByApplication().collectList().toFuture() + /** + * Delivers task created event. + */ @Suppress("unused") @EventHandler fun on(event: TaskCreatedEngineEvent, metaData: MetaData) { @@ -217,6 +221,9 @@ class MongoViewService( .block() } + /** + * Delivers task assigned event. + */ @Suppress("unused") @EventHandler fun on(event: TaskAssignedEngineEvent, metaData: MetaData) { @@ -231,6 +238,9 @@ class MongoViewService( .block() } + /** + * Delivers task completed event. + */ @Suppress("unused") @EventHandler fun on(event: TaskCompletedEngineEvent, metaData: MetaData) { @@ -238,6 +248,9 @@ class MongoViewService( deleteTask(event.id, event.sourceReference.applicationName) } + /** + * Delivers task deleted event. + */ @Suppress("unused") @EventHandler fun on(event: TaskDeletedEngineEvent, metaData: MetaData) { @@ -263,6 +276,9 @@ class MongoViewService( .block() } + /** + * Delivers task attribute changed event. + */ @Suppress("unused") @EventHandler fun on(event: TaskAttributeUpdatedEngineEvent, metaData: MetaData) { @@ -277,6 +293,9 @@ class MongoViewService( .block() } + /** + * Delivers task group changed event. + */ @Suppress("unused") @EventHandler fun on(event: TaskCandidateGroupChanged, metaData: MetaData) { @@ -291,6 +310,9 @@ class MongoViewService( .block() } + /** + * Delivers task user changed event. + */ @Suppress("unused") @EventHandler fun on(event: TaskCandidateUserChanged, metaData: MetaData) { @@ -305,6 +327,9 @@ class MongoViewService( .block() } + /** + * Delivers data entry created event. + */ @Suppress("unused") @EventHandler fun on(event: DataEntryCreatedEvent, metaData: MetaData) { @@ -314,6 +339,9 @@ class MongoViewService( .block() } + /** + * Delivers data entry updated event. + */ @Suppress("unused") @EventHandler fun on(event: DataEntryUpdatedEvent, metaData: MetaData) { diff --git a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/Criteria.kt b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/Criteria.kt deleted file mode 100755 index df14b11cc..000000000 --- a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/Criteria.kt +++ /dev/null @@ -1,99 +0,0 @@ -package io.holunda.polyflow.view.mongo.task - -import io.holunda.polyflow.view.Task -import java.time.Instant -import kotlin.reflect.full.memberProperties - -const val EQUALS = "=" -const val GREATER = ">" -const val LESS = "<" -const val TASK_PREFIX = "task." -val OPERATORS = Regex("[$EQUALS$LESS$GREATER]") - -/** - * Forms criteria from string filters. - */ -fun toCriteria(filters: List) = filters.map { toCriterion(it) }.filter { it !is Criterion.EmptyCriterion } - -/** - * Forms a single criteria from string filter. - */ -internal fun toCriterion(filter: String): Criterion { - - require(filter.isNotBlank()) { "Failed to create criteria from empty filter '$filter'." } - - if (!filter.contains(OPERATORS)) { - return Criterion.EmptyCriterion - } - - val segments = when { - filter.contains(EQUALS) -> filter.split(EQUALS).plus(EQUALS) - filter.contains(GREATER) -> filter.split(GREATER).plus(GREATER) - filter.contains(LESS) -> filter.split(LESS).plus(LESS) - else -> listOf() - } - require(segments.size == 3 && !segments[0].isBlank() && !segments[1].isBlank()) { "Failed to create criteria from $filter." } - - return if (isTaskAttribute(segments[0])) { - Criterion.TaskCriterion(name = segments[0].substring(TASK_PREFIX.length), value = segments[1], operator = segments[2]) - } else { - Criterion.DataEntryCriterion(name = segments[0], value = segments[1], operator = segments[2]) - } -} - - -/** - * Checks is a property is a task attribute. - */ -internal fun isTaskAttribute(propertyName: String): Boolean = - propertyName.startsWith(TASK_PREFIX) - && propertyName.length > TASK_PREFIX.length - && Task::class.memberProperties.map { it.name }.contains(propertyName.substring(TASK_PREFIX.length)) - -/** - * Criterion. - */ -sealed class Criterion(open val name: String, open val value: String, open val operator: String) { - - /** - * Value converter for criteria. - */ - fun typedValue(): Any = - when (this.name) { - "priority" -> this.value.toInt() - "createTime", "dueDate", "followUpDate" -> Instant.parse(this.value) - else -> this.value - } - - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Criterion) return false - - if (name != other.name) return false - if (value != other.value) return false - if (operator != other.operator) return false - - return true - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + value.hashCode() - result = 31 * result + operator.hashCode() - return result - } - - object EmptyCriterion : Criterion("empty", "no value", "none") - - /** - * Criterion on task. - */ - data class TaskCriterion(override val name: String, override val value: String, override val operator: String = EQUALS) : Criterion(name, value, operator) - - /** - * Criterion on data entry. - */ - data class DataEntryCriterion(override val name: String, override val value: String, override val operator: String = EQUALS) : - Criterion(name, value, operator) -} diff --git a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtension.kt b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtension.kt index b5fb0ed68..2a8172edf 100644 --- a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtension.kt +++ b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtension.kt @@ -1,6 +1,7 @@ package io.holunda.polyflow.view.mongo.task import io.holunda.polyflow.view.auth.User +import io.holunda.polyflow.view.filter.Criterion import org.springframework.data.domain.Pageable import reactor.core.publisher.Flux diff --git a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtensionImpl.kt b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtensionImpl.kt index 6819d0a35..d85190cf4 100644 --- a/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtensionImpl.kt +++ b/view/mongo/src/main/kotlin/io/holunda/polyflow/view/mongo/task/TaskWithDataEntriesRepositoryExtensionImpl.kt @@ -1,6 +1,10 @@ package io.holunda.polyflow.view.mongo.task import io.holunda.polyflow.view.auth.User +import io.holunda.polyflow.view.filter.Criterion +import io.holunda.polyflow.view.filter.EQUALS +import io.holunda.polyflow.view.filter.GREATER +import io.holunda.polyflow.view.filter.LESS import io.holunda.polyflow.view.mongo.data.DataEntryDocument import mu.KLogging import org.springframework.data.domain.Pageable diff --git a/view/mongo/src/test/kotlin/io/holunda/polyflow/view/mongo/filter/FilterTest.kt b/view/mongo/src/test/kotlin/io/holunda/polyflow/view/mongo/filter/FilterTest.kt deleted file mode 100755 index 3bf37c74d..000000000 --- a/view/mongo/src/test/kotlin/io/holunda/polyflow/view/mongo/filter/FilterTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package io.holunda.polyflow.view.mongo.filter - -import io.holunda.polyflow.view.mongo.task.Criterion -import io.holunda.polyflow.view.mongo.task.Criterion.DataEntryCriterion -import io.holunda.polyflow.view.mongo.task.Criterion.TaskCriterion -import io.holunda.polyflow.view.mongo.task.EQUALS -import io.holunda.polyflow.view.mongo.task.isTaskAttribute -import io.holunda.polyflow.view.mongo.task.toCriteria -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import org.junit.jupiter.api.assertThrows - -class FilterTest { - - private val filtersList = listOf("task.name${EQUALS}myName", "task.assignee${EQUALS}kermit", "dataAttr1${EQUALS}value", "dataAttr2${EQUALS}another") - - @Test - fun `should classify properties`() { - assertThat(isTaskAttribute("task.id")).isTrue - assertThat(isTaskAttribute("task.name")).isTrue - assertThat(isTaskAttribute("task.assignee")).isTrue - - assertThat(isTaskAttribute("task.")).isFalse - assertThat(isTaskAttribute("assignee")).isFalse - assertThat(isTaskAttribute("someOther")).isFalse - assertThat(isTaskAttribute("described")).isFalse - } - - @Test - fun `should create criteria`() { - - val criteria = toCriteria(filtersList) - - assertThat(criteria).isNotNull - assertThat(criteria.size).isEqualTo(4) - assertThat(criteria).containsExactlyElementsOf( - listOf( - TaskCriterion("name", "myName"), TaskCriterion("assignee", "kermit"), - DataEntryCriterion("dataAttr1", "value"), DataEntryCriterion("dataAttr2", "another") - ) - ) - } - - @Test - fun `should fail to create criteria`() { - assertThrows { - toCriteria(listOf("$EQUALS$EQUALS")) - } - } - - @Test - fun `should fail to create criteria 2`() { - assertThrows { - toCriteria(listOf("${EQUALS}some$EQUALS")) - } - } - - @Test - fun `should ignore wrong format`() { - assertThat(toCriteria(listOf("noEQUALS"))).isEmpty() - } -} - From 8fbee2d2095d993c5f604121164290a7c70df07d Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Thu, 12 Aug 2021 14:31:55 +0200 Subject: [PATCH 19/27] killed codacy warning --- .../kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt index 05ce3809c..224492ac8 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt @@ -17,6 +17,9 @@ class DataEntryId( ) : Serializable { companion object { + /** + * Factgory method to construct the Data Entry Id from a string. + */ operator fun invoke(identity: String): DataEntryId = identity.split(":").let { require(it.size >= 2) { "Illegal identity format, expecting :, received '$identity'" } DataEntryId(entryType = it[0], entryId = it.subList(1, it.size).joinToString(":")) From 9fe09afc6a82fd610f29e951c018d69eebe27ee3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 16:01:07 +0000 Subject: [PATCH 20/27] chore(deps): bump openapi-generator-maven-plugin from 5.2.0 to 5.2.1 Bumps openapi-generator-maven-plugin from 5.2.0 to 5.2.1. --- updated-dependencies: - dependency-name: org.openapitools:openapi-generator-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pom.xml b/examples/pom.xml index 01459638e..ac6351627 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -188,7 +188,7 @@ org.openapitools openapi-generator-maven-plugin - 5.2.0 + 5.2.1 From 505f8cde0c24d1beee00831d6853d10be2a82e19 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Mon, 16 Aug 2021 23:30:51 +0200 Subject: [PATCH 21/27] implemented filter query for attributes --- docs/reference-guide/components/view-jpa.md | 34 ++++++++----- .../kotlin/rest/impl/TaskResourceITest.kt | 2 - view/jpa/pom.xml | 5 ++ .../view/jpa/JpaPolyflowViewService.kt | 3 -- .../view/jpa/PolyflowJpaViewConfiguration.kt | 11 ++++- .../polyflow/view/jpa/data/Converters.kt | 24 ++++----- .../polyflow/view/jpa/data/DataEntryEntity.kt | 13 ++--- .../polyflow/view/jpa/data/DataEntryId.kt | 2 +- .../view/jpa/data/DataEntryRepository.kt | 21 +++----- .../view/jpa/data/PayloadAttribute.kt | 36 ++++++++++++++ .../polyflow/view/jpa/data/ProtocolElement.kt | 3 +- .../view/jpa/data/DataEntryRepositoryITest.kt | 49 ++++++++++--------- .../src/test/resources/application-itest.yaml | 8 +-- 13 files changed, 134 insertions(+), 77 deletions(-) create mode 100644 view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt diff --git a/docs/reference-guide/components/view-jpa.md b/docs/reference-guide/components/view-jpa.md index 6d104f97c..c52831a9e 100644 --- a/docs/reference-guide/components/view-jpa.md +++ b/docs/reference-guide/components/view-jpa.md @@ -7,13 +7,13 @@ pageId: view-jpa ### Purpose -The JPA View is component responsible for creating read-projections of tasks and business data entries. It implements -the Taskpool and Datapool View API and persists the projection as document collections in a RDBMS using JPA. It is a useful -if the JPA persistence is used in the project already. +The JPA View is component responsible for creating read-projections of tasks and business data entries. It currently implements +Datapool View API (Taskpool API will follow) and persists the projection as entities and relations in a RDBMS using JPA. It is a useful +if the JPA persistence is already used in the project setup. ### Features -* stores representation of enriched tasks, process definitions and business data entries +* stores representation of business data entries * provides single query API @@ -30,8 +30,7 @@ In order to activate the JPA View implementation, please include the following d ``` The implementation relies on Spring Data JPA and needs to activate those. - -In addition, configure a a JPA connection to database using `application.properties` or `application.yaml`: +In addition, configure a JPA connection to database using `application.properties` or `application.yaml`: ```yml spring: @@ -39,20 +38,33 @@ spring: jpa: ``` +The JPA view uses a special facility for creating search indexes on unstructured payload. You can provide some +configuration of this indexing process by the following configuration options: + +```yml +polyflow.view.jpa: + payload-attribute-level-limit: 2 +``` + +### Logging + The view implementation provides runtime details using standard logging facility. If you want to increase the logging level, please setup it e.g. in your `application.yaml`: - ```yml logging.level.io.holunda.polyflow.view.jpa: DEBUG ``` + ### Tables The JPA View uses several tables to store the results. These are: -* AUTHORIZATION_PRINCIPAL: table for authorization principals (users, groups) -* DATA_ENTRY: table for business data entries -* DATA_ENTRY_AUTHORIZATIONS: table for authorization information of data entries (relation) -* PROTOCOL_ENTRY: table for data entry protocol entry (users, groups) +* PLF_DATA_ENTRY: table for business data entries +* PLF_DATA_ENTRY_AUTHORIZATIONS: table for authorization information of data entries +* PLF_DATA_ENTRY_PAYLOAD_ATTRIBUTES: table for data entry protocol entry (users, groups) +* PLF_DATA_ENTRY_PROTOCOL: table for data entry protocol entry (users, groups) * TRACKING_TOKEN: table for Axon Tracking Tokens + +IF you are interested in DDLs for the view, feel free to generate one using the following call of Apache Maven `mvn clean test -DskipTests -Pgenerate-sql -f view/jpa`. +Currently, DDLs for the databases H2, MSSQL and PostgreSQL are generated. diff --git a/examples/components/tasklist-backend/src/test/kotlin/rest/impl/TaskResourceITest.kt b/examples/components/tasklist-backend/src/test/kotlin/rest/impl/TaskResourceITest.kt index 1c410e8c8..be19fcfdf 100644 --- a/examples/components/tasklist-backend/src/test/kotlin/rest/impl/TaskResourceITest.kt +++ b/examples/components/tasklist-backend/src/test/kotlin/rest/impl/TaskResourceITest.kt @@ -147,10 +147,8 @@ internal class TaskResourceITest { @Test fun `should undefer task`() { - val date = Instant.now() val taskId = UUID.randomUUID().toString() val task = testTask(taskId, assignee = "kermit") - val json = objectMapper.writeValueAsString(OffsetDateTime.ofInstant(date, UTC)) whenever(taskServiceGateway.getTask(any())).thenReturn(task) diff --git a/view/jpa/pom.xml b/view/jpa/pom.xml index 1923755fb..0d04356b1 100755 --- a/view/jpa/pom.xml +++ b/view/jpa/pom.xml @@ -175,6 +175,11 @@ + + org.javassist + javassist + 3.28.0-GA + javax.xml.bind jaxb-api diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt index cf265b274..d138aa244 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt @@ -54,12 +54,9 @@ class JpaPolyflowViewService( val specification = criteria.toSpecification() val elements = if (specification != null) { - // alternative dataEntryRepository.findAll(specification.and(isAuthorizedFor(authorizedPrincipals))) - // dataEntryRepository.findAllByAuthorizedPrincipalsIn(authorizationQuery, specification) } else { dataEntryRepository.findAll(isAuthorizedFor(authorizedPrincipals)) - // dataEntryRepository.findAllByAuthorizedPrincipalsIn(authorizedPrincipals.map { it.toString() }) } return constructResponse(elements, query) diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt index 42db13c74..468fff51d 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/PolyflowJpaViewConfiguration.kt @@ -1,11 +1,20 @@ package io.holunda.polyflow.view.jpa +import io.holunda.polyflow.view.jpa.data.DataEntryEntity +import io.holunda.polyflow.view.jpa.data.DataEntryRepository +import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration import org.springframework.data.jpa.repository.config.EnableJpaRepositories -@Configuration @EnableConfigurationProperties(PolyflowJpaViewProperties::class) @ComponentScan +@EntityScan( + basePackageClasses = [DataEntryEntity::class] +) +@EnableJpaRepositories( + basePackageClasses = [DataEntryRepository::class] +) +@Configuration class PolyflowJpaViewConfiguration diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt index f8c9f8536..af09fd285 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/Converters.kt @@ -70,7 +70,7 @@ fun DataEntryState.toState() = DataEntryStateEmbeddable(processingType = this.pr fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: RevisionValue, limit: Int) = DataEntryEntity( dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), payload = this.payload.toPayloadJson(objectMapper), - payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toMutableSet(), + payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { attr -> PayloadAttribute(attr) }.toMutableSet(), name = this.name, applicationName = this.applicationName, type = this.type, @@ -87,7 +87,7 @@ fun DataEntryCreatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re authorizedPrincipals = AuthorizationChange.applyUserAuthorization(mutableSetOf(), this.authorizations).map { user(it).toString() } .plus(AuthorizationChange.applyGroupAuthorization(mutableSetOf(), this.authorizations).map { group(it).toString() }).toMutableSet(), ).apply { - this.protocol = this.protocol.addModification(this, this@toEntity.createModification, this@toEntity.state).toMutableList() + this.protocol = this.protocol.addModification(this, this@toEntity.createModification, this@toEntity.state) } /** @@ -97,7 +97,7 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re DataEntryEntity( dataEntryId = DataEntryId(entryType = this.entryType, entryId = this.entryId), payload = this.payload.toPayloadJson(objectMapper), - payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toMutableSet(), + payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { attr -> PayloadAttribute(attr) }.toMutableSet(), name = this.name, applicationName = this.applicationName, type = this.type, @@ -117,7 +117,7 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } else { oldEntry.also { it.payload = this.payload.toPayloadJson(objectMapper) - it.payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { "${it.key}=${it.value}" }.toMutableSet() + it.payloadAttributes = this.payload.toJsonPathsWithValues(limit).map { attr -> PayloadAttribute(attr) }.toMutableSet() it.name = this.name it.applicationName = this.applicationName it.type = this.type @@ -129,11 +129,11 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re AuthorizationChange.applyUserAuthorization( it.authorizedPrincipals.asUsernames(), this.authorizations - ).map { user(it).toString() } + ).map { user -> user(user).toString() } .plus(AuthorizationChange.applyGroupAuthorization( it.authorizedPrincipals.asGroupnames(), this.authorizations - ).map { group(it).toString() }) + ).map { group -> group(group).toString() }) .toMutableSet() it.revision = if (revisionValue != RevisionValue.NO_REVISION) { revisionValue.revision @@ -142,14 +142,14 @@ fun DataEntryUpdatedEvent.toEntity(objectMapper: ObjectMapper, revisionValue: Re } } }.apply { - this.protocol = this.protocol.addModification(this, this@toEntity.updateModification, this@toEntity.state).toMutableList() + this.protocol = this.protocol.addModification(this, this@toEntity.updateModification, this@toEntity.state) } /** * Adds a modification to the protocol, if it doesn't exist in the protocol already, comparing all protocol element properties * besides the technical id. */ -fun List.addModification(dataEntry: DataEntryEntity, modification: Modification, state: DataEntryState) = +fun MutableList.addModification(dataEntry: DataEntryEntity, modification: Modification, state: DataEntryState) = ProtocolElement( dataEntry = dataEntry, time = modification.time.toInstant(), @@ -158,9 +158,9 @@ fun List.addModification(dataEntry: DataEntryEntity, modificati logDetails = modification.logNotes, state = state.toState() ).let { protocolElement -> - if (dataEntry.protocol.any { existing -> existing.same(protocolElement) }) { - this - } else { - this.plus(protocolElement) + this.apply { + if (dataEntry.protocol.none { existing -> existing.same(protocolElement) }) { + this.add(protocolElement) + } } } diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt index 349ddc678..d1696f9d1 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryEntity.kt @@ -7,7 +7,8 @@ import javax.persistence.* /** * Entity to store data entries. */ -@Entity(name = "DATA_ENTRY") +@Entity +@Table(name = "PLF_DATA_ENTRY") class DataEntryEntity( @EmbeddedId var dataEntryId: DataEntryId, @@ -35,7 +36,7 @@ class DataEntryEntity( @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( - name = "DATA_ENTRY_AUTHORIZATIONS", + name = "PLF_DATA_ENTRY_AUTHORIZATIONS", joinColumns = [ JoinColumn(name = "ENTRY_TYPE", referencedColumnName = "ENTRY_TYPE"), JoinColumn(name = "ENTRY_ID", referencedColumnName = "ENTRY_ID"), @@ -46,16 +47,16 @@ class DataEntryEntity( @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( - name = "DATA_PAYLOAD_ATTRIBUTES", + name = "PLF_DATA_ENTRY_PAYLOAD_ATTRIBUTES", joinColumns = [ JoinColumn(name = "ENTRY_TYPE", referencedColumnName = "ENTRY_TYPE"), JoinColumn(name = "ENTRY_ID", referencedColumnName = "ENTRY_ID"), ] ) - @Column(name = "PAYLOAD_ATTRIBUTE", nullable = false) - var payloadAttributes: MutableSet = mutableSetOf(), + var payloadAttributes: MutableSet = mutableSetOf(), - @OneToMany(mappedBy = "dataEntry", orphanRemoval = true, cascade = [CascadeType.ALL], targetEntity = ProtocolElement::class, fetch = FetchType.EAGER) + + @OneToMany(mappedBy = "dataEntry", orphanRemoval = true, cascade = [CascadeType.ALL], fetch = FetchType.EAGER) var protocol: MutableList = mutableListOf(), @Lob diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt index 224492ac8..a6e143968 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryId.kt @@ -18,7 +18,7 @@ class DataEntryId( companion object { /** - * Factgory method to construct the Data Entry Id from a string. + * Factory method to construct the Data Entry Id from a string. */ operator fun invoke(identity: String): DataEntryId = identity.split(":").let { require(it.size >= 2) { "Illegal identity format, expecting :, received '$identity'" } diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt index 44ab413e9..fb00ec375 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepository.kt @@ -41,21 +41,16 @@ interface DataEntryRepository : CrudRepository, Jp */ fun hasPayloadAttribute(name: String, value: String): Specification = Specification { dataEntry, _, builder -> - builder.isMember( - "$name=$value", - dataEntry.get>(DataEntryEntity::payloadAttributes.name) + val join = dataEntry.join>(DataEntryEntity::payloadAttributes.name) + val pathEquals = builder.equal( + join.get(PayloadAttribute::path.name), + name ) - } - - /** - * Specification for checking authorization. - */ - fun isAuthorizedFor(principal: AuthorizationPrincipal): Specification = - Specification { dataEntry, _, builder -> - builder.isMember( - "${principal.type}:${principal.name}", - dataEntry.get>(DataEntryEntity::authorizedPrincipals.name) + val valueEquals = builder.equal( + join.get(PayloadAttribute::value.name), + value ) + builder.and(pathEquals, valueEquals) } /** diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt new file mode 100644 index 000000000..7eb86b879 --- /dev/null +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt @@ -0,0 +1,36 @@ +package io.holunda.polyflow.view.jpa.data + +import java.io.Serializable +import javax.persistence.Column +import javax.persistence.Embeddable + +@Embeddable +class PayloadAttribute( + @Column(name = "PATH", nullable = false) + var path: String?, + @Column(name = "VALUE", nullable = false) + var value: String? +) : Serializable { + + companion object { + operator fun invoke(entry: Map.Entry) = PayloadAttribute(path = entry.key, value = entry.value.toString()) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as PayloadAttribute + + if (path != other.path) return false + if (value != other.value) return false + + return true + } + + override fun hashCode(): Int { + var result = path?.hashCode() ?: 0 + result = 31 * result + (value?.hashCode() ?: 0) + return result + } +} diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt index ec18b8277..a9102759b 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/ProtocolElement.kt @@ -4,7 +4,8 @@ import java.time.Instant import java.util.* import javax.persistence.* -@Entity(name = "PROTOCOL") +@Entity +@Table(name = "PLF_DATA_ENTRY_PROTOCOL") class ProtocolElement( @Id @Column(name = "ID") diff --git a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt index 2d06dd5fa..a23f4c190 100644 --- a/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt +++ b/view/jpa/src/test/kotlin/io/holunda/polyflow/view/jpa/data/DataEntryRepositoryITest.kt @@ -56,7 +56,7 @@ internal class DataEntryRepositoryITest { ) } val json = jacksonObjectMapper().writeValueAsString(payload1) - val payloadAttributes = payload1.toJsonPathsWithValues().map { "${it.key}=${it.value}" }.toSet() + val payloadAttributes = payload1.toJsonPathsWithValues().map { PayloadAttribute(it) }.toSet() val payload2 = createVariables().apply { putAll( @@ -68,7 +68,7 @@ internal class DataEntryRepositoryITest { ) } val json2 = jacksonObjectMapper().writeValueAsString(payload2) - val payloadAttributes2 = payload2.toJsonPathsWithValues().map { "${it.key}=${it.value}" }.toSet() + val payloadAttributes2 = payload2.toJsonPathsWithValues().map { PayloadAttribute(it) }.toSet() val state1 = ProcessingType.IN_PROGRESS.of("In progress") val state2 = ProcessingType.IN_PROGRESS.of("In review") @@ -88,7 +88,7 @@ internal class DataEntryRepositoryITest { user("piggy").toString(), ), payload = json, - payloadAttributes = payloadAttributes.toMutableSet() + payloadAttributes = payloadAttributes.toMutableSet(), ).apply { this.protocol = mutableListOf( ProtocolElement( @@ -148,7 +148,7 @@ internal class DataEntryRepositoryITest { } @Test - fun `should find data entries by authorization`() { + fun `should find data entries by authorization dsl method`() { val muppets = dataEntryRepository.findAllByAuthorizedPrincipalsIn(setOf(group("muppets").toString())) assertThat(muppets).containsExactly(dataEntry) @@ -176,33 +176,36 @@ internal class DataEntryRepositoryITest { @Test fun `should find by filter`() { - val byStateInProgress = dataEntryRepository.findAll(where(hasState("In progress"))) - assertThat(byStateInProgress).containsExactlyInAnyOrderElementsOf(listOf(dataEntry)) - - val byStateInReview = dataEntryRepository.findAll(where(hasState("In review"))) - assertThat(byStateInReview).containsExactlyInAnyOrderElementsOf(listOf(dataEntry2)) - - val byProcessingTypeInProgress = dataEntryRepository.findAll(where(hasProcessingType(ProcessingType.IN_PROGRESS))) - assertThat(byProcessingTypeInProgress).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) - - val byProcessingTypeCompleted = dataEntryRepository.findAll(where(hasProcessingType(ProcessingType.COMPLETED))) - assertThat(byProcessingTypeCompleted).isEmpty() - - val byPayloadFilterByChildKeyNumberValue = dataEntryRepository.findAll(where(hasPayloadAttribute("child.key-number", "42"))) - assertThat(byPayloadFilterByChildKeyNumberValue).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) +// val byStateInProgress = dataEntryRepository.findAll(where(hasState("In progress"))) +// assertThat(byStateInProgress).containsExactlyInAnyOrderElementsOf(listOf(dataEntry)) +// +// val byStateInReview = dataEntryRepository.findAll(where(hasState("In review"))) +// assertThat(byStateInReview).containsExactlyInAnyOrderElementsOf(listOf(dataEntry2)) +// +// val byProcessingTypeInProgress = dataEntryRepository.findAll(where(hasProcessingType(ProcessingType.IN_PROGRESS))) +// assertThat(byProcessingTypeInProgress).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) +// +// val byProcessingTypeCompleted = dataEntryRepository.findAll(where(hasProcessingType(ProcessingType.COMPLETED))) +// assertThat(byProcessingTypeCompleted).isEmpty() +// +//// val byPayloadFilterByChildKeyNumberValue = dataEntryRepository.findAll(where(hasPayloadAttribute("child.key-number", "42"))) +// val byPayloadFilterByChildKeyNumberValue = dataEntryRepository.findAll(where(hasPayloadAttribute("child.key-number", "42"))) +// assertThat(byPayloadFilterByChildKeyNumberValue).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) val byPayloadFilterByChildKeyValue = dataEntryRepository.findAll( +// where(hasPayloadAttribute("child.key", "value")) +// .and(hasPayloadAttribute("id", dataEntry.dataEntryId.entryId)) where(hasPayloadAttribute("child.key", "value")) .and(hasPayloadAttribute("id", dataEntry.dataEntryId.entryId)) ) assertThat(byPayloadFilterByChildKeyValue).containsExactlyInAnyOrderElementsOf(listOf(dataEntry)) - val piggy = dataEntryRepository.findAll(isAuthorizedFor(setOf(user("piggy")))) - assertThat(piggy).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) - - val kermitOrAvengers = dataEntryRepository.findAll(isAuthorizedFor(setOf(user("kermit"), group("avengers")))) - assertThat(kermitOrAvengers).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) +// val piggy = dataEntryRepository.findAll(isAuthorizedFor(setOf(user("piggy")))) +// assertThat(piggy).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) +// +// val kermitOrAvengers = dataEntryRepository.findAll(isAuthorizedFor(setOf(user("kermit"), group("avengers")))) +// assertThat(kermitOrAvengers).containsExactlyInAnyOrderElementsOf(listOf(dataEntry, dataEntry2)) } diff --git a/view/jpa/src/test/resources/application-itest.yaml b/view/jpa/src/test/resources/application-itest.yaml index 0588e6877..204ca5601 100644 --- a/view/jpa/src/test/resources/application-itest.yaml +++ b/view/jpa/src/test/resources/application-itest.yaml @@ -1,10 +1,10 @@ spring: jpa: open-in-view: true # disable JPA warning - show-sql: false + show-sql: true -# logger: -# level: -# org.hibernate.type: TRACE # activate this and generic ROOT logger to see SQL and binding +logger: + level: + org.hibernate.type: TRACE # activate this and generic ROOT logger to see SQL and binding From 0fea96169508c9dfc57af1ded063e71e65c188b5 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Tue, 17 Aug 2021 10:01:07 +0200 Subject: [PATCH 22/27] more docs, killed some codacy warnings --- docs/examples/scenarios/single-node.md | 3 ++- docs/reference-guide/components/index.md | 2 ++ docs/reference-guide/components/view-api.md | 16 ++++++++++++++++ .../reference-guide/components/view-filtering.md | 9 +++++++++ mkdocs.yml | 2 ++ .../polyflow/view/jpa/data/PayloadAttribute.kt | 7 +++++++ 6 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 docs/reference-guide/components/view-api.md create mode 100644 docs/reference-guide/components/view-filtering.md diff --git a/docs/examples/scenarios/single-node.md b/docs/examples/scenarios/single-node.md index 0c725ea00..1e9412391 100644 --- a/docs/examples/scenarios/single-node.md +++ b/docs/examples/scenarios/single-node.md @@ -2,7 +2,8 @@ title: Scenario for running on a single node --- -This example demonstrates the usage of the Camunda BPM Taskpool deployed in one single node and is built as a SpringBoot application described in the [Deployment](../../indroduction/deployment.md) section. +This example demonstrates the usage of the Camunda BPM Taskpool deployed in one single node and is built as a SpringBoot application +described in the [Deployment](../../introduction/deployment.md) section. ### System Requirements diff --git a/docs/reference-guide/components/index.md b/docs/reference-guide/components/index.md index 1dbcf8a38..1bb65a9b3 100644 --- a/docs/reference-guide/components/index.md +++ b/docs/reference-guide/components/index.md @@ -43,10 +43,12 @@ process application, process platform or even completely separately. View Components are responsible for creation of a unified read-only projection of process definitions, process instances, process variables, user tasks and business data items. They are typically deployed as a part of the process platform. +* [View API](view-api.md) * [In-Memory View](view-simple.md) * [JPA View](view-jpa.md) * [Mongo DB View](view-mongo.md) * [Property Form URL Resolver](view-form-url-resolver.md) +* [View Filtering](view-filtering.md) ## Other Components diff --git a/docs/reference-guide/components/view-api.md b/docs/reference-guide/components/view-api.md new file mode 100644 index 000000000..261555ecd --- /dev/null +++ b/docs/reference-guide/components/view-api.md @@ -0,0 +1,16 @@ +--- +title: View API +pageId: view-api +--- + +## View API + +### Purpose + +The Polyflow View API defines the interfaces of the task-pool and data-pool query API. It defines the main queries of the common read-projections. +Its main purpose is to create a public stable API which is independent of the implementations. There are multiple implementations available: + +* [In-Memory View](view-simple.md) +* [JPA View](view-jpa.md) +* [Mongo DB View](view-mongo.md) + diff --git a/docs/reference-guide/components/view-filtering.md b/docs/reference-guide/components/view-filtering.md new file mode 100644 index 000000000..a522a40fc --- /dev/null +++ b/docs/reference-guide/components/view-filtering.md @@ -0,0 +1,9 @@ +## View Filtering + +### Purpose + +The purpose of the filtering component is to provide main common implementations for handling requests of filtering of view results in form of +attribute filters (like `attribute=value&attrubute2=value2&task.name=taskname`). Especially it defines the main concepts like `Criteria` and `Operator` +and allows classification of those. This component is helpful if you implement your own view. + + diff --git a/mkdocs.yml b/mkdocs.yml index 2682dd21c..3bdb942fe 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -118,6 +118,8 @@ nav: - Mongo DB View: reference-guide/components/view-mongo.md - Cockpit View: reference-guide/components/view-cockpit.md - Form URL Resolver: reference-guide/components/view-form-url-resolver.md + - View-API: reference-guide/components/view-api.md + - View Filtering: reference-guide/components/view-filtering.md - Other Components: - Tasklist URL Resolver: reference-guide/components/other-tasklist-url-resolver.md - Variable Serializer: reference-guide/components/other-variable-serializer.md diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt index 7eb86b879..06c963ee4 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/data/PayloadAttribute.kt @@ -4,6 +4,9 @@ import java.io.Serializable import javax.persistence.Column import javax.persistence.Embeddable +/** + * Represents a searchable payload attribute consisting of the (JSON)-path pf the attribute and its value. + */ @Embeddable class PayloadAttribute( @Column(name = "PATH", nullable = false) @@ -13,6 +16,10 @@ class PayloadAttribute( ) : Serializable { companion object { + /** + * Factory method to create payload attributes out of map entries. + * Check VariableSerializer methods how such map entries can be created. + */ operator fun invoke(entry: Map.Entry) = PayloadAttribute(path = entry.key, value = entry.value.toString()) } From 5fa4ea6f7fc423c515ff9a5ba23d32e45d186e19 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Tue, 17 Aug 2021 10:20:04 +0200 Subject: [PATCH 23/27] more docs --- .github/ISSUE_TEMPLATE/1_bug_report.md | 12 +++---- .github/ISSUE_TEMPLATE/2_feature_request.md | 6 ++-- README.md | 34 +++++++++---------- .../core/DataPoolCoreConfiguration.kt | 3 ++ .../upcaster/DataEntryEventUpcaster.kt | 4 ++- .../process/ProcessDefinitionAggregate.kt | 7 +++- .../ProcessDefinitionEventUpcasters.kt | 4 +-- ...tEventUpcasterForPatchingSourceRefernce.kt | 7 ++-- examples/README.md | 16 --------- .../view/jpa/JpaPolyflowViewService.kt | 3 ++ 10 files changed, 48 insertions(+), 48 deletions(-) delete mode 100644 examples/README.md diff --git a/.github/ISSUE_TEMPLATE/1_bug_report.md b/.github/ISSUE_TEMPLATE/1_bug_report.md index 4f524a54c..f27866ad9 100644 --- a/.github/ISSUE_TEMPLATE/1_bug_report.md +++ b/.github/ISSUE_TEMPLATE/1_bug_report.md @@ -9,12 +9,12 @@ assignees: zambrovski ### Steps to reproduce -* camunda-bpm-taskpool version: -* Camunda BPM version: -* JDK version: -* Operating system: -* Complete executable reproducer: (e.g. GitHub Repo) -* Steps: (what exactly are you doing with the above reproducer?) + * camunda-bpm-taskpool version: + * Camunda BPM version: + * JDK version: + * Operating system: + * Complete executable reproducer: (e.g. GitHub Repo) + * Steps: (what exactly are you doing with the above reproducer?) ### Expected behaviour diff --git a/.github/ISSUE_TEMPLATE/2_feature_request.md b/.github/ISSUE_TEMPLATE/2_feature_request.md index e16cb81f0..bcf60f2ff 100644 --- a/.github/ISSUE_TEMPLATE/2_feature_request.md +++ b/.github/ISSUE_TEMPLATE/2_feature_request.md @@ -9,9 +9,9 @@ assignees: ### Scenario -* camunda-bpm-taskpool version: -* Camunda BPM version: -* Description of your use case: (detailed description or executable reproducer, e.g. GitHub repo) + * camunda-bpm-taskpool version: + * Camunda BPM version: + * Description of your use case: (detailed description or executable reproducer, e.g. GitHub repo) ### Current Behaviour diff --git a/README.md b/README.md index fdf047f42..66069be5a 100755 --- a/README.md +++ b/README.md @@ -9,33 +9,33 @@ > A component library for building enterprise-wide process platforms with multiple process engines like Camunda BPM. -In the last five years, we built different process applications on behalf of the customer several times. It turned out that some of the issues occurred every +In the last five years, we built different process applications on behalf of the customer several times. It turned out that some issues occurred every time during the implementation. -Polyflow Hero +![Hero Image](docs/img/polyflow-hero-530x406.png) These were: -* coping with performance issues if big amount of tasks is available -* creating high-performance custom queries for pre-loading process variables for tasks -* creating high-performance custom queries to pre-load business data associated with the process instance -* high-performance retrieving a list of tasks from several process engines -* repetitive queries with same result -* creating an archive view for business data items handled during the process execution -* creating an audit log of changes performed on business data items + * coping with performance issues if big amount of tasks is available + * creating high-performance custom queries for pre-loading process variables for tasks + * creating high-performance custom queries to pre-load business data associated with the process instance + * high-performance retrieving a list of tasks from several process engines + * repetitive queries with same result + * creating an archive view for business data items handled during the process execution + * creating an audit log of changes performed on business data items We decided to stop repetitive work and release an open-source library which builds a foundation for solving these problems. ### Features -* User task API providing attributes important for processing -* Mirroring tasks: provides a copy of all tasks in the system -* Reacts on all task life cycle events fired by the process engine -* High performance queries: creates read-optimized projections including task-, process- and business data -* Centralized task list: allows collecting tasks from multiple engines -* Data enrichment: enrich tasks with business data -* Data entries API providing attributes important for processing -* Audit-Trail creation on business event emission + * User task API providing attributes important for processing + * Mirroring tasks: provides a copy of all tasks in the system + * Reacts on all task life cycle events fired by the process engine + * High performance queries: creates read-optimized projections including task-, process- and business data + * Centralized task list: allows collecting tasks from multiple engines + * Data enrichment: enrich tasks with business data + * Data entries API providing attributes important for processing + * Audit-Trail creation on business event emission ### Where to start diff --git a/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/DataPoolCoreConfiguration.kt b/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/DataPoolCoreConfiguration.kt index 4ce60f2b4..5c38eba5e 100755 --- a/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/DataPoolCoreConfiguration.kt +++ b/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/DataPoolCoreConfiguration.kt @@ -8,6 +8,9 @@ import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration +/** + * Configuration of polyflow data pool core. + */ @Configuration @ComponentScan class DataPoolCoreConfiguration { diff --git a/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/business/upcaster/DataEntryEventUpcaster.kt b/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/business/upcaster/DataEntryEventUpcaster.kt index dbbb444b2..bb1048ad2 100644 --- a/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/business/upcaster/DataEntryEventUpcaster.kt +++ b/core/datapool/datapool-core/src/main/kotlin/io/holunda/camunda/datapool/core/business/upcaster/DataEntryEventUpcaster.kt @@ -8,7 +8,9 @@ import org.dom4j.Document import org.dom4j.DocumentHelper import org.springframework.stereotype.Component - +/** + * Upcaster adding attributes applicationName, type and name to the event as specified in revision 2. + */ @Component class DataEntryCreatedEventUpcaster : SingleEventUpcaster() { diff --git a/core/taskpool/taskpool-core/src/main/kotlin/io/holunda/camunda/taskpool/core/process/ProcessDefinitionAggregate.kt b/core/taskpool/taskpool-core/src/main/kotlin/io/holunda/camunda/taskpool/core/process/ProcessDefinitionAggregate.kt index 0b90782d5..1f81b1118 100644 --- a/core/taskpool/taskpool-core/src/main/kotlin/io/holunda/camunda/taskpool/core/process/ProcessDefinitionAggregate.kt +++ b/core/taskpool/taskpool-core/src/main/kotlin/io/holunda/camunda/taskpool/core/process/ProcessDefinitionAggregate.kt @@ -8,6 +8,9 @@ import org.axonframework.modelling.command.AggregateIdentifier import org.axonframework.modelling.command.AggregateLifecycle import org.axonframework.spring.stereotype.Aggregate +/** + * Command model responsible for commands on process definition. + */ @Aggregate class ProcessDefinitionAggregate() { @@ -34,7 +37,9 @@ class ProcessDefinitionAggregate() { )) } - + /** + * React on registration of a new process definition. + */ @EventSourcingHandler fun on(event: ProcessDefinitionRegisteredEvent) { this.processDefinitionId = event.processDefinitionId diff --git a/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt b/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt index ad9cabc2f..1564b918b 100644 --- a/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt +++ b/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/definition/ProcessDefinitionEventUpcasters.kt @@ -8,7 +8,7 @@ import org.dom4j.Document import java.util.function.Function /** - * Upcaster to put revision 1 into the event and remove uneeded attributes. + * Upcaster to put revision 1 into the event and remove unneeded attributes. */ @AnnotatedEventUpcaster("io.holunda.camunda.taskpool.api.task.ProcessDefinitionRegisteredEvent") class ProcessDefinitionEventXMLNullTo1Upcaster : AnnotationBasedSingleEventUpcaster() { @@ -38,7 +38,7 @@ class ProcessDefinitionEventXMLNullTo1Upcaster : AnnotationBasedSingleEventUpcas } /** - * Upcaster to put revision 1 into the event and remove uneeded attributes. + * Upcaster to put revision 1 into the event and remove unneeded attributes. */ @AnnotatedEventUpcaster("io.holunda.camunda.taskpool.api.task.ProcessDefinitionRegisteredEvent", representationContentType = JSON) class ProcessDefinitionEventJSONNullTo1Upcaster : AnnotationBasedSingleEventUpcaster() { diff --git a/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/task/TaskEventEventUpcasterForPatchingSourceRefernce.kt b/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/task/TaskEventEventUpcasterForPatchingSourceRefernce.kt index 74b8e6337..18959cffe 100644 --- a/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/task/TaskEventEventUpcasterForPatchingSourceRefernce.kt +++ b/core/taskpool/taskpool-event/src/main/kotlin/io/holunda/camunda/taskpool/upcast/task/TaskEventEventUpcasterForPatchingSourceRefernce.kt @@ -5,7 +5,6 @@ import io.holunda.camunda.taskpool.upcast.definition.AnnotationBasedSingleEventU import mu.KLogging import org.axonframework.serialization.SimpleSerializedType import org.axonframework.serialization.upcasting.event.IntermediateEventRepresentation -import org.dom4j.Attribute import org.dom4j.Document /** @@ -171,7 +170,8 @@ abstract class AbstractSourceReferenceElementRemovingUpcaster : AnnotationBasedS override fun doUpcast(representation: IntermediateEventRepresentation): IntermediateEventRepresentation { return representation.upcastPayload( SimpleSerializedType(getType(), "4"), - Document::class.java) { document -> + Document::class.java + ) { document -> document.apply { TAG_NAMES.forEach { removeWrongElement(document, it) @@ -180,6 +180,9 @@ abstract class AbstractSourceReferenceElementRemovingUpcaster : AnnotationBasedS } } + /** + * Retrieves the type of resulting object. + */ abstract fun getType(): String private fun removeWrongElement(document: Document, tagName: String) { diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index b2ef3bd11..000000000 --- a/examples/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Taskpool Examples - -Please check the [User guide](https://www.holunda.io/camunda-bpm-taskpool/wiki/user-guide/) first. - -The following Maven modules are part of taskpool examples: - -* process-backend: example process application backend module, using Camunda SpringBoot starter. -* process-forms: example process application forms, built using Angular. -* tasklist-angular: example implementation of a tasklist using Angular. -* tasklist-api: REST API for building a tasklist frontend. -* tasklist-backend: example implementation of a tasklist backend, based on SpringBoot -* users: fake user service used for authentication - -## Scenarios - -Checkout the scenarios folder to find example applications for different [Usage scenarios](https://www.holunda.io/camunda-bpm-taskpool/wiki/user-guide/scenarios/). diff --git a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt index d138aa244..7742547b5 100644 --- a/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt +++ b/view/jpa/src/main/kotlin/io/holunda/polyflow/view/jpa/JpaPolyflowViewService.kt @@ -24,6 +24,9 @@ import org.axonframework.queryhandling.QueryUpdateEmitter import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component +/** + * Implementation of the Polyflow View API using JPA to create the persistence model. + */ @Component @ProcessingGroup(PROCESSING_GROUP) class JpaPolyflowViewService( From ec14f571ccecf3a3b9f0936213056f686f5aaef4 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Tue, 17 Aug 2021 10:30:11 +0200 Subject: [PATCH 24/27] codacy improvements --- .github/ISSUE_TEMPLATE/1_bug_report.md | 12 ++++++------ .github/ISSUE_TEMPLATE/2_feature_request.md | 6 +++--- README.md | 4 ++-- codacy.yaml | 7 +++++++ 4 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 codacy.yaml diff --git a/.github/ISSUE_TEMPLATE/1_bug_report.md b/.github/ISSUE_TEMPLATE/1_bug_report.md index f27866ad9..4f524a54c 100644 --- a/.github/ISSUE_TEMPLATE/1_bug_report.md +++ b/.github/ISSUE_TEMPLATE/1_bug_report.md @@ -9,12 +9,12 @@ assignees: zambrovski ### Steps to reproduce - * camunda-bpm-taskpool version: - * Camunda BPM version: - * JDK version: - * Operating system: - * Complete executable reproducer: (e.g. GitHub Repo) - * Steps: (what exactly are you doing with the above reproducer?) +* camunda-bpm-taskpool version: +* Camunda BPM version: +* JDK version: +* Operating system: +* Complete executable reproducer: (e.g. GitHub Repo) +* Steps: (what exactly are you doing with the above reproducer?) ### Expected behaviour diff --git a/.github/ISSUE_TEMPLATE/2_feature_request.md b/.github/ISSUE_TEMPLATE/2_feature_request.md index bcf60f2ff..e16cb81f0 100644 --- a/.github/ISSUE_TEMPLATE/2_feature_request.md +++ b/.github/ISSUE_TEMPLATE/2_feature_request.md @@ -9,9 +9,9 @@ assignees: ### Scenario - * camunda-bpm-taskpool version: - * Camunda BPM version: - * Description of your use case: (detailed description or executable reproducer, e.g. GitHub repo) +* camunda-bpm-taskpool version: +* Camunda BPM version: +* Description of your use case: (detailed description or executable reproducer, e.g. GitHub repo) ### Current Behaviour diff --git a/README.md b/README.md index 66069be5a..84a7e82cb 100755 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ In the last five years, we built different process applications on behalf of the customer several times. It turned out that some issues occurred every time during the implementation. -![Hero Image](docs/img/polyflow-hero-530x406.png) - These were: * coping with performance issues if big amount of tasks is available @@ -24,6 +22,8 @@ These were: * creating an archive view for business data items handled during the process execution * creating an audit log of changes performed on business data items +![Polyflow Hero](docs/img/polyflow-hero-530x406.png) + We decided to stop repetitive work and release an open-source library which builds a foundation for solving these problems. ### Features diff --git a/codacy.yaml b/codacy.yaml new file mode 100644 index 000000000..bf9d8bb0d --- /dev/null +++ b/codacy.yaml @@ -0,0 +1,7 @@ +--- +engines: + duplications: + exclude_paths: + - "**/src/test/kotlin/**" +exclude_paths: + - ".github/ISSUE_TEMPLATE/**" From e47b2cabff71fe0436954f442fe4ef000e734d42 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Tue, 17 Aug 2021 10:30:56 +0200 Subject: [PATCH 25/27] fix indent in README --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 84a7e82cb..4fb376748 100755 --- a/README.md +++ b/README.md @@ -14,13 +14,13 @@ time during the implementation. These were: - * coping with performance issues if big amount of tasks is available - * creating high-performance custom queries for pre-loading process variables for tasks - * creating high-performance custom queries to pre-load business data associated with the process instance - * high-performance retrieving a list of tasks from several process engines - * repetitive queries with same result - * creating an archive view for business data items handled during the process execution - * creating an audit log of changes performed on business data items +* coping with performance issues if big amount of tasks is available +* creating high-performance custom queries for pre-loading process variables for tasks +* creating high-performance custom queries to pre-load business data associated with the process instance +* high-performance retrieving a list of tasks from several process engines +* repetitive queries with same result +* creating an archive view for business data items handled during the process execution +* creating an audit log of changes performed on business data items ![Polyflow Hero](docs/img/polyflow-hero-530x406.png) @@ -28,14 +28,14 @@ We decided to stop repetitive work and release an open-source library which buil ### Features - * User task API providing attributes important for processing - * Mirroring tasks: provides a copy of all tasks in the system - * Reacts on all task life cycle events fired by the process engine - * High performance queries: creates read-optimized projections including task-, process- and business data - * Centralized task list: allows collecting tasks from multiple engines - * Data enrichment: enrich tasks with business data - * Data entries API providing attributes important for processing - * Audit-Trail creation on business event emission +* User task API providing attributes important for processing +* Mirroring tasks: provides a copy of all tasks in the system +* Reacts on all task life cycle events fired by the process engine +* High performance queries: creates read-optimized projections including task-, process- and business data +* Centralized task list: allows collecting tasks from multiple engines +* Data enrichment: enrich tasks with business data +* Data entries API providing attributes important for processing +* Audit-Trail creation on business event emission ### Where to start From 6b07401935522c9b60860d18a4c5552bc870948d Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Tue, 17 Aug 2021 10:56:35 +0200 Subject: [PATCH 26/27] Update versions for release --- bom/datapool-dependencies/pom.xml | 2 +- bom/parent/pom.xml | 4 ++-- bom/taskpool-dependencies/pom.xml | 4 ++-- core/datapool/datapool-api/pom.xml | 2 +- core/datapool/datapool-core/pom.xml | 2 +- core/datapool/datapool-event/pom.xml | 2 +- core/datapool/pom.xml | 2 +- core/taskpool/pom.xml | 2 +- core/taskpool/taskpool-api/pom.xml | 2 +- core/taskpool/taskpool-core/pom.xml | 2 +- core/taskpool/taskpool-event/pom.xml | 2 +- examples/components/process-backend/pom.xml | 2 +- examples/components/process-forms/pom.xml | 2 +- examples/components/tasklist-angular/pom.xml | 2 +- examples/components/tasklist-backend/pom.xml | 2 +- examples/components/tasklist-reactive-backend/pom.xml | 2 +- examples/components/users/pom.xml | 2 +- examples/pom.xml | 2 +- .../cockpit-application/application/pom.xml | 2 +- .../cockpit-application/backend/pom.xml | 2 +- .../cockpit-application/frontend/pom.xml | 2 +- .../distributed-axon-server/cockpit-application/pom.xml | 2 +- examples/scenarios/distributed-axon-server/pom.xml | 2 +- .../distributed-axon-server/process-application/pom.xml | 2 +- .../distributed-axon-server/taskpool-application/pom.xml | 2 +- examples/scenarios/pom.xml | 2 +- examples/scenarios/single-node/pom.xml | 2 +- integration/camunda-bpm/engine-client/pom.xml | 2 +- integration/camunda-bpm/pom.xml | 2 +- integration/camunda-bpm/springboot-starter/pom.xml | 2 +- integration/camunda-bpm/taskpool-collector/pom.xml | 2 +- integration/common/datapool-sender/pom.xml | 2 +- integration/common/pom.xml | 2 +- integration/common/tasklist-url-resolver/pom.xml | 2 +- integration/common/taskpool-sender/pom.xml | 2 +- integration/common/variable-serializer/pom.xml | 2 +- pom.xml | 2 +- view/filtering/pom.xml | 2 +- view/form-url-resolver/pom.xml | 2 +- view/jpa/pom.xml | 2 +- view/mongo/pom.xml | 2 +- view/pom.xml | 2 +- view/simple/pom.xml | 2 +- view/view-api/pom.xml | 2 +- 44 files changed, 46 insertions(+), 46 deletions(-) diff --git a/bom/datapool-dependencies/pom.xml b/bom/datapool-dependencies/pom.xml index 4add9d6c9..a7c758a7c 100644 --- a/bom/datapool-dependencies/pom.xml +++ b/bom/datapool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../parent/pom.xml diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index 3660cd2e7..04cb5fcf2 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../pom.xml @@ -119,7 +119,7 @@ io.holunda.polyflow polyflow-view-filtering - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT diff --git a/bom/taskpool-dependencies/pom.xml b/bom/taskpool-dependencies/pom.xml index 64134c4b9..fed293434 100644 --- a/bom/taskpool-dependencies/pom.xml +++ b/bom/taskpool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../parent/pom.xml @@ -42,7 +42,7 @@ io.holunda.polyflow polyflow-camunda-bpm-taskpool-collector - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT io.holunda.polyflow diff --git a/core/datapool/datapool-api/pom.xml b/core/datapool/datapool-api/pom.xml index c5ff64f6a..6a9d10c3c 100755 --- a/core/datapool/datapool-api/pom.xml +++ b/core/datapool/datapool-api/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-datapool-api diff --git a/core/datapool/datapool-core/pom.xml b/core/datapool/datapool-core/pom.xml index 3c41760c7..846127a72 100755 --- a/core/datapool/datapool-core/pom.xml +++ b/core/datapool/datapool-core/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-datapool-core diff --git a/core/datapool/datapool-event/pom.xml b/core/datapool/datapool-event/pom.xml index 273512983..cb0ea7d4c 100755 --- a/core/datapool/datapool-event/pom.xml +++ b/core/datapool/datapool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-datapool-event diff --git a/core/datapool/pom.xml b/core/datapool/pom.xml index 5394303e7..894107947 100755 --- a/core/datapool/pom.xml +++ b/core/datapool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../bom/parent/pom.xml diff --git a/core/taskpool/pom.xml b/core/taskpool/pom.xml index e2be57be0..bfcc200d9 100755 --- a/core/taskpool/pom.xml +++ b/core/taskpool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../bom/parent/pom.xml diff --git a/core/taskpool/taskpool-api/pom.xml b/core/taskpool/taskpool-api/pom.xml index c5d2e9fdd..c259e2bbe 100755 --- a/core/taskpool/taskpool-api/pom.xml +++ b/core/taskpool/taskpool-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-taskpool-api diff --git a/core/taskpool/taskpool-core/pom.xml b/core/taskpool/taskpool-core/pom.xml index 92fc648cd..f5e7d5d0e 100755 --- a/core/taskpool/taskpool-core/pom.xml +++ b/core/taskpool/taskpool-core/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-taskpool-core diff --git a/core/taskpool/taskpool-event/pom.xml b/core/taskpool/taskpool-event/pom.xml index d8064b1f8..88939f370 100755 --- a/core/taskpool/taskpool-event/pom.xml +++ b/core/taskpool/taskpool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-taskpool-event diff --git a/examples/components/process-backend/pom.xml b/examples/components/process-backend/pom.xml index bd4255063..e57be35ea 100755 --- a/examples/components/process-backend/pom.xml +++ b/examples/components/process-backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../pom.xml diff --git a/examples/components/process-forms/pom.xml b/examples/components/process-forms/pom.xml index 699f955ff..154204698 100755 --- a/examples/components/process-forms/pom.xml +++ b/examples/components/process-forms/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../pom.xml diff --git a/examples/components/tasklist-angular/pom.xml b/examples/components/tasklist-angular/pom.xml index 38b76216b..14cc81e76 100755 --- a/examples/components/tasklist-angular/pom.xml +++ b/examples/components/tasklist-angular/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../pom.xml diff --git a/examples/components/tasklist-backend/pom.xml b/examples/components/tasklist-backend/pom.xml index e4e7e0c46..f466d0c7e 100755 --- a/examples/components/tasklist-backend/pom.xml +++ b/examples/components/tasklist-backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../pom.xml diff --git a/examples/components/tasklist-reactive-backend/pom.xml b/examples/components/tasklist-reactive-backend/pom.xml index d63efef15..82810c47d 100755 --- a/examples/components/tasklist-reactive-backend/pom.xml +++ b/examples/components/tasklist-reactive-backend/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../pom.xml diff --git a/examples/components/users/pom.xml b/examples/components/users/pom.xml index a1ee10a9f..2a188a810 100644 --- a/examples/components/users/pom.xml +++ b/examples/components/users/pom.xml @@ -7,7 +7,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../pom.xml diff --git a/examples/pom.xml b/examples/pom.xml index ac6351627..4cf14f806 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../pom.xml diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml index 16127b71a..df06ade65 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-example-cockpit diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml index c04ff5e12..aaa0aaa96 100644 --- a/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-example-cockpit-backend diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml index ce9b8e26b..f1bdc912e 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-example-cockpit-frontend diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml index a2d337165..06019216e 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-example-cockpit-root diff --git a/examples/scenarios/distributed-axon-server/pom.xml b/examples/scenarios/distributed-axon-server/pom.xml index c7d656132..c4b59f055 100755 --- a/examples/scenarios/distributed-axon-server/pom.xml +++ b/examples/scenarios/distributed-axon-server/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-example-scenario-distributed-axon-server diff --git a/examples/scenarios/distributed-axon-server/process-application/pom.xml b/examples/scenarios/distributed-axon-server/process-application/pom.xml index e08eb81e8..30366df25 100755 --- a/examples/scenarios/distributed-axon-server/process-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/process-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT example-distributed-axon-server-process-application diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml index 0eed1a5f4..03c320fe4 100755 --- a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT example-distributed-axon-server-taskpool-application diff --git a/examples/scenarios/pom.xml b/examples/scenarios/pom.xml index d71cd074c..389ae0a09 100755 --- a/examples/scenarios/pom.xml +++ b/examples/scenarios/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-example-scenario-root diff --git a/examples/scenarios/single-node/pom.xml b/examples/scenarios/single-node/pom.xml index fb909b5f7..4efc63b19 100755 --- a/examples/scenarios/single-node/pom.xml +++ b/examples/scenarios/single-node/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-example-scenario-single-node diff --git a/integration/camunda-bpm/engine-client/pom.xml b/integration/camunda-bpm/engine-client/pom.xml index 170fed453..5a0d483ef 100644 --- a/integration/camunda-bpm/engine-client/pom.xml +++ b/integration/camunda-bpm/engine-client/pom.xml @@ -4,7 +4,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-camunda-bpm-engine-client diff --git a/integration/camunda-bpm/pom.xml b/integration/camunda-bpm/pom.xml index 3c8127f34..bd66a4e9f 100644 --- a/integration/camunda-bpm/pom.xml +++ b/integration/camunda-bpm/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../bom/parent/pom.xml diff --git a/integration/camunda-bpm/springboot-starter/pom.xml b/integration/camunda-bpm/springboot-starter/pom.xml index 4a05e7c2e..9cee2b0e6 100755 --- a/integration/camunda-bpm/springboot-starter/pom.xml +++ b/integration/camunda-bpm/springboot-starter/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-camunda-bpm-springboot-starter diff --git a/integration/camunda-bpm/taskpool-collector/pom.xml b/integration/camunda-bpm/taskpool-collector/pom.xml index 83ebc2e4a..224604437 100755 --- a/integration/camunda-bpm/taskpool-collector/pom.xml +++ b/integration/camunda-bpm/taskpool-collector/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-camunda-bpm-taskpool-collector diff --git a/integration/common/datapool-sender/pom.xml b/integration/common/datapool-sender/pom.xml index d5a40e114..c9ddc4c62 100755 --- a/integration/common/datapool-sender/pom.xml +++ b/integration/common/datapool-sender/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-datapool-sender diff --git a/integration/common/pom.xml b/integration/common/pom.xml index e5496527b..2718007ae 100755 --- a/integration/common/pom.xml +++ b/integration/common/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../../bom/parent/pom.xml diff --git a/integration/common/tasklist-url-resolver/pom.xml b/integration/common/tasklist-url-resolver/pom.xml index 34b5973f3..0e9930629 100644 --- a/integration/common/tasklist-url-resolver/pom.xml +++ b/integration/common/tasklist-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-tasklist-url-resolver diff --git a/integration/common/taskpool-sender/pom.xml b/integration/common/taskpool-sender/pom.xml index 9227da71b..d763ac3f1 100755 --- a/integration/common/taskpool-sender/pom.xml +++ b/integration/common/taskpool-sender/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-taskpool-sender diff --git a/integration/common/variable-serializer/pom.xml b/integration/common/variable-serializer/pom.xml index 6c600419f..7b3aa7786 100755 --- a/integration/common/variable-serializer/pom.xml +++ b/integration/common/variable-serializer/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-variable-serializer diff --git a/pom.xml b/pom.xml index 1787abc4c..e456cdf85 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-root - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT pom POM: ${project.artifactId} diff --git a/view/filtering/pom.xml b/view/filtering/pom.xml index decb8376f..82e8492f5 100644 --- a/view/filtering/pom.xml +++ b/view/filtering/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-view-filtering diff --git a/view/form-url-resolver/pom.xml b/view/form-url-resolver/pom.xml index 0c8b89d11..82cf5f449 100644 --- a/view/form-url-resolver/pom.xml +++ b/view/form-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-form-url-resolver diff --git a/view/jpa/pom.xml b/view/jpa/pom.xml index 0d04356b1..b61ee40ab 100755 --- a/view/jpa/pom.xml +++ b/view/jpa/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-view-jpa diff --git a/view/mongo/pom.xml b/view/mongo/pom.xml index 1953ee45f..39177c361 100755 --- a/view/mongo/pom.xml +++ b/view/mongo/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-view-mongo diff --git a/view/pom.xml b/view/pom.xml index b9b35c0ba..d731609c3 100644 --- a/view/pom.xml +++ b/view/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT ../bom/parent/pom.xml diff --git a/view/simple/pom.xml b/view/simple/pom.xml index 09eed4f81..ad3295bf5 100755 --- a/view/simple/pom.xml +++ b/view/simple/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-view-simple diff --git a/view/view-api/pom.xml b/view/view-api/pom.xml index b964f96e0..a9bb1989a 100755 --- a/view/view-api/pom.xml +++ b/view/view-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.0.3-SNAPSHOT + 3.1.0-SNAPSHOT polyflow-view-api From 9f5b642b6bea1cf07e1d4e6a3495a39c6fe52f64 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Tue, 17 Aug 2021 11:04:04 +0200 Subject: [PATCH 27/27] Update for next development version --- bom/datapool-dependencies/pom.xml | 2 +- bom/parent/pom.xml | 4 ++-- bom/taskpool-dependencies/pom.xml | 4 ++-- core/datapool/datapool-api/pom.xml | 2 +- core/datapool/datapool-core/pom.xml | 2 +- core/datapool/datapool-event/pom.xml | 2 +- core/datapool/pom.xml | 2 +- core/taskpool/pom.xml | 2 +- core/taskpool/taskpool-api/pom.xml | 2 +- core/taskpool/taskpool-core/pom.xml | 2 +- core/taskpool/taskpool-event/pom.xml | 2 +- examples/components/process-backend/pom.xml | 2 +- examples/components/process-forms/pom.xml | 2 +- examples/components/tasklist-angular/pom.xml | 2 +- examples/components/tasklist-backend/pom.xml | 2 +- examples/components/tasklist-reactive-backend/pom.xml | 2 +- examples/components/users/pom.xml | 2 +- examples/pom.xml | 2 +- .../cockpit-application/application/pom.xml | 2 +- .../cockpit-application/backend/pom.xml | 2 +- .../cockpit-application/frontend/pom.xml | 2 +- .../distributed-axon-server/cockpit-application/pom.xml | 2 +- examples/scenarios/distributed-axon-server/pom.xml | 2 +- .../distributed-axon-server/process-application/pom.xml | 2 +- .../distributed-axon-server/taskpool-application/pom.xml | 2 +- examples/scenarios/pom.xml | 2 +- examples/scenarios/single-node/pom.xml | 2 +- integration/camunda-bpm/engine-client/pom.xml | 2 +- integration/camunda-bpm/pom.xml | 2 +- integration/camunda-bpm/springboot-starter/pom.xml | 2 +- integration/camunda-bpm/taskpool-collector/pom.xml | 2 +- integration/common/datapool-sender/pom.xml | 2 +- integration/common/pom.xml | 2 +- integration/common/tasklist-url-resolver/pom.xml | 2 +- integration/common/taskpool-sender/pom.xml | 2 +- integration/common/variable-serializer/pom.xml | 2 +- pom.xml | 2 +- view/filtering/pom.xml | 2 +- view/form-url-resolver/pom.xml | 2 +- view/jpa/pom.xml | 2 +- view/mongo/pom.xml | 2 +- view/pom.xml | 2 +- view/simple/pom.xml | 2 +- view/view-api/pom.xml | 2 +- 44 files changed, 46 insertions(+), 46 deletions(-) diff --git a/bom/datapool-dependencies/pom.xml b/bom/datapool-dependencies/pom.xml index a7c758a7c..c7f6b828a 100644 --- a/bom/datapool-dependencies/pom.xml +++ b/bom/datapool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.1.0-SNAPSHOT + 3.1.0 ../parent/pom.xml diff --git a/bom/parent/pom.xml b/bom/parent/pom.xml index 04cb5fcf2..d29f168a4 100644 --- a/bom/parent/pom.xml +++ b/bom/parent/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-root - 3.1.0-SNAPSHOT + 3.1.0 ../../pom.xml @@ -119,7 +119,7 @@ io.holunda.polyflow polyflow-view-filtering - 3.1.0-SNAPSHOT + 3.1.0 diff --git a/bom/taskpool-dependencies/pom.xml b/bom/taskpool-dependencies/pom.xml index fed293434..bfd0e7531 100644 --- a/bom/taskpool-dependencies/pom.xml +++ b/bom/taskpool-dependencies/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-parent - 3.1.0-SNAPSHOT + 3.1.0 ../parent/pom.xml @@ -42,7 +42,7 @@ io.holunda.polyflow polyflow-camunda-bpm-taskpool-collector - 3.1.0-SNAPSHOT + 3.1.0 io.holunda.polyflow diff --git a/core/datapool/datapool-api/pom.xml b/core/datapool/datapool-api/pom.xml index 6a9d10c3c..8139f2233 100755 --- a/core/datapool/datapool-api/pom.xml +++ b/core/datapool/datapool-api/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-datapool-api diff --git a/core/datapool/datapool-core/pom.xml b/core/datapool/datapool-core/pom.xml index 846127a72..d35d25233 100755 --- a/core/datapool/datapool-core/pom.xml +++ b/core/datapool/datapool-core/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-datapool-core diff --git a/core/datapool/datapool-event/pom.xml b/core/datapool/datapool-event/pom.xml index cb0ea7d4c..b17c805f3 100755 --- a/core/datapool/datapool-event/pom.xml +++ b/core/datapool/datapool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-datapool-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-datapool-event diff --git a/core/datapool/pom.xml b/core/datapool/pom.xml index 894107947..537af7092 100755 --- a/core/datapool/pom.xml +++ b/core/datapool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../bom/parent/pom.xml diff --git a/core/taskpool/pom.xml b/core/taskpool/pom.xml index bfcc200d9..d17b1eb5e 100755 --- a/core/taskpool/pom.xml +++ b/core/taskpool/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../bom/parent/pom.xml diff --git a/core/taskpool/taskpool-api/pom.xml b/core/taskpool/taskpool-api/pom.xml index c259e2bbe..6368440fa 100755 --- a/core/taskpool/taskpool-api/pom.xml +++ b/core/taskpool/taskpool-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-taskpool-api diff --git a/core/taskpool/taskpool-core/pom.xml b/core/taskpool/taskpool-core/pom.xml index f5e7d5d0e..d0d84ea76 100755 --- a/core/taskpool/taskpool-core/pom.xml +++ b/core/taskpool/taskpool-core/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-taskpool-core diff --git a/core/taskpool/taskpool-event/pom.xml b/core/taskpool/taskpool-event/pom.xml index 88939f370..22aa16a4a 100755 --- a/core/taskpool/taskpool-event/pom.xml +++ b/core/taskpool/taskpool-event/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-taskpool-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-taskpool-event diff --git a/examples/components/process-backend/pom.xml b/examples/components/process-backend/pom.xml index e57be35ea..866632ea3 100755 --- a/examples/components/process-backend/pom.xml +++ b/examples/components/process-backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../pom.xml diff --git a/examples/components/process-forms/pom.xml b/examples/components/process-forms/pom.xml index 154204698..599b35c49 100755 --- a/examples/components/process-forms/pom.xml +++ b/examples/components/process-forms/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../pom.xml diff --git a/examples/components/tasklist-angular/pom.xml b/examples/components/tasklist-angular/pom.xml index 14cc81e76..0e170c67d 100755 --- a/examples/components/tasklist-angular/pom.xml +++ b/examples/components/tasklist-angular/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../pom.xml diff --git a/examples/components/tasklist-backend/pom.xml b/examples/components/tasklist-backend/pom.xml index f466d0c7e..dd36ad0a2 100755 --- a/examples/components/tasklist-backend/pom.xml +++ b/examples/components/tasklist-backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../pom.xml diff --git a/examples/components/tasklist-reactive-backend/pom.xml b/examples/components/tasklist-reactive-backend/pom.xml index 82810c47d..3ccedf833 100755 --- a/examples/components/tasklist-reactive-backend/pom.xml +++ b/examples/components/tasklist-reactive-backend/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../pom.xml diff --git a/examples/components/users/pom.xml b/examples/components/users/pom.xml index 2a188a810..63bbf8e89 100644 --- a/examples/components/users/pom.xml +++ b/examples/components/users/pom.xml @@ -7,7 +7,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../pom.xml diff --git a/examples/pom.xml b/examples/pom.xml index 4cf14f806..b6a161226 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-root - 3.1.0-SNAPSHOT + 3.1.0 ../pom.xml diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml index df06ade65..14f7cc4e2 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.1.0-SNAPSHOT + 3.1.0 polyflow-example-cockpit diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml index aaa0aaa96..f30b50b0a 100644 --- a/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/backend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.1.0-SNAPSHOT + 3.1.0 polyflow-example-cockpit-backend diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml index f1bdc912e..254b6610f 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/frontend/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-cockpit-root - 3.1.0-SNAPSHOT + 3.1.0 polyflow-example-cockpit-frontend diff --git a/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml b/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml index 06019216e..bc2bd6687 100755 --- a/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/cockpit-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.1.0-SNAPSHOT + 3.1.0 polyflow-example-cockpit-root diff --git a/examples/scenarios/distributed-axon-server/pom.xml b/examples/scenarios/distributed-axon-server/pom.xml index c4b59f055..7c4d69353 100755 --- a/examples/scenarios/distributed-axon-server/pom.xml +++ b/examples/scenarios/distributed-axon-server/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-root - 3.1.0-SNAPSHOT + 3.1.0 polyflow-example-scenario-distributed-axon-server diff --git a/examples/scenarios/distributed-axon-server/process-application/pom.xml b/examples/scenarios/distributed-axon-server/process-application/pom.xml index 30366df25..91b893837 100755 --- a/examples/scenarios/distributed-axon-server/process-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/process-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.1.0-SNAPSHOT + 3.1.0 example-distributed-axon-server-process-application diff --git a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml index 03c320fe4..260c0b976 100755 --- a/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml +++ b/examples/scenarios/distributed-axon-server/taskpool-application/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-distributed-axon-server - 3.1.0-SNAPSHOT + 3.1.0 example-distributed-axon-server-taskpool-application diff --git a/examples/scenarios/pom.xml b/examples/scenarios/pom.xml index 389ae0a09..573728983 100755 --- a/examples/scenarios/pom.xml +++ b/examples/scenarios/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-examples-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-example-scenario-root diff --git a/examples/scenarios/single-node/pom.xml b/examples/scenarios/single-node/pom.xml index 4efc63b19..a05f9686f 100755 --- a/examples/scenarios/single-node/pom.xml +++ b/examples/scenarios/single-node/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-example-scenario-root - 3.1.0-SNAPSHOT + 3.1.0 polyflow-example-scenario-single-node diff --git a/integration/camunda-bpm/engine-client/pom.xml b/integration/camunda-bpm/engine-client/pom.xml index 5a0d483ef..42f8bb690 100644 --- a/integration/camunda-bpm/engine-client/pom.xml +++ b/integration/camunda-bpm/engine-client/pom.xml @@ -4,7 +4,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-camunda-bpm-engine-client diff --git a/integration/camunda-bpm/pom.xml b/integration/camunda-bpm/pom.xml index bd66a4e9f..8f1174df3 100644 --- a/integration/camunda-bpm/pom.xml +++ b/integration/camunda-bpm/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../bom/parent/pom.xml diff --git a/integration/camunda-bpm/springboot-starter/pom.xml b/integration/camunda-bpm/springboot-starter/pom.xml index 9cee2b0e6..fb8c64764 100755 --- a/integration/camunda-bpm/springboot-starter/pom.xml +++ b/integration/camunda-bpm/springboot-starter/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-camunda-bpm-springboot-starter diff --git a/integration/camunda-bpm/taskpool-collector/pom.xml b/integration/camunda-bpm/taskpool-collector/pom.xml index 224604437..941da8e1e 100755 --- a/integration/camunda-bpm/taskpool-collector/pom.xml +++ b/integration/camunda-bpm/taskpool-collector/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-camunda-bpm-engine-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-camunda-bpm-taskpool-collector diff --git a/integration/common/datapool-sender/pom.xml b/integration/common/datapool-sender/pom.xml index c9ddc4c62..88fa09479 100755 --- a/integration/common/datapool-sender/pom.xml +++ b/integration/common/datapool-sender/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-datapool-sender diff --git a/integration/common/pom.xml b/integration/common/pom.xml index 2718007ae..1c936c441 100755 --- a/integration/common/pom.xml +++ b/integration/common/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.1.0-SNAPSHOT + 3.1.0 ../../bom/parent/pom.xml diff --git a/integration/common/tasklist-url-resolver/pom.xml b/integration/common/tasklist-url-resolver/pom.xml index 0e9930629..7ca66a79e 100644 --- a/integration/common/tasklist-url-resolver/pom.xml +++ b/integration/common/tasklist-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-tasklist-url-resolver diff --git a/integration/common/taskpool-sender/pom.xml b/integration/common/taskpool-sender/pom.xml index d763ac3f1..27476f613 100755 --- a/integration/common/taskpool-sender/pom.xml +++ b/integration/common/taskpool-sender/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-taskpool-sender diff --git a/integration/common/variable-serializer/pom.xml b/integration/common/variable-serializer/pom.xml index 7b3aa7786..018597cc4 100755 --- a/integration/common/variable-serializer/pom.xml +++ b/integration/common/variable-serializer/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-integration-common-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-variable-serializer diff --git a/pom.xml b/pom.xml index e456cdf85..883eb87a5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-root - 3.1.0-SNAPSHOT + 3.1.0 pom POM: ${project.artifactId} diff --git a/view/filtering/pom.xml b/view/filtering/pom.xml index 82e8492f5..4ed89b2d3 100644 --- a/view/filtering/pom.xml +++ b/view/filtering/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-view-filtering diff --git a/view/form-url-resolver/pom.xml b/view/form-url-resolver/pom.xml index 82cf5f449..f4c9b8de5 100644 --- a/view/form-url-resolver/pom.xml +++ b/view/form-url-resolver/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-form-url-resolver diff --git a/view/jpa/pom.xml b/view/jpa/pom.xml index b61ee40ab..d69013ee5 100755 --- a/view/jpa/pom.xml +++ b/view/jpa/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-view-jpa diff --git a/view/mongo/pom.xml b/view/mongo/pom.xml index 39177c361..442d3dd30 100755 --- a/view/mongo/pom.xml +++ b/view/mongo/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-view-mongo diff --git a/view/pom.xml b/view/pom.xml index d731609c3..00100feb4 100644 --- a/view/pom.xml +++ b/view/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-parent - 3.1.0-SNAPSHOT + 3.1.0 ../bom/parent/pom.xml diff --git a/view/simple/pom.xml b/view/simple/pom.xml index ad3295bf5..02204783c 100755 --- a/view/simple/pom.xml +++ b/view/simple/pom.xml @@ -5,7 +5,7 @@ io.holunda.polyflow polyflow-view-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-view-simple diff --git a/view/view-api/pom.xml b/view/view-api/pom.xml index a9bb1989a..904272fa7 100755 --- a/view/view-api/pom.xml +++ b/view/view-api/pom.xml @@ -6,7 +6,7 @@ io.holunda.polyflow polyflow-view-parent - 3.1.0-SNAPSHOT + 3.1.0 polyflow-view-api