From 463c16959fc16dbcc5d2b1b08f37b797b7d003ad Mon Sep 17 00:00:00 2001 From: Pierre-Yves Date: Tue, 4 Jun 2024 17:11:47 -0700 Subject: [PATCH] 3.1 upload connector (#132) * new page monitoring * UpLoad Jar from Content page * Move to 8.3 version * Add a banner * Produce the same element-template * OAuth integration * Organize import * Improve interface to show the connection * Remove Ping in the list on demand * Update information any time * Rename application and remove mustache --- README.md | 5 + doc/CherryArchitecture.md | 10 + doc/DeveloperGuide/README.md | 30 +- doc/todo.md | 59 +++ element-templates/Ping connector.json | 4 +- pom.xml | 117 ++++- src/main/frontend/src/App.js | 2 +- src/main/frontend/src/App.test.js | 4 +- src/main/frontend/src/CherryApp.jsx | 2 +- .../frontend/src/component/ControllerPage.jsx | 7 +- src/main/frontend/src/content/Content.jsx | 144 +++++- src/main/frontend/src/dashboard/Dashboard.jsx | 14 +- .../frontend/src/dashboard/Monitoring.jsx | 91 ++++ .../frontend/src/dashboard/RunnerChart.jsx | 3 +- .../src/dashboard/RunnerDashboard.jsx | 75 ++-- .../src/dashboard/RunnerMonitoring.jsx | 22 +- .../frontend/src/definition/Definition.jsx | 2 +- .../src/definition/RunnerDefinition.jsx | 7 +- .../frontend/src/definition/RunnerHeader.jsx | 1 + src/main/frontend/src/index.css | 6 +- src/main/frontend/src/index.js | 2 +- src/main/frontend/src/logo.svg | 9 +- .../src/operationlog/OperationLog.jsx | 2 +- .../frontend/src/parameter/Parameters.jsx | 128 ++++-- src/main/frontend/src/reportWebVitals.js | 2 +- src/main/frontend/src/secrets/Secrets.jsx | 8 +- .../frontend/src/services/HttpResponse.jsx | 26 +- .../frontend/src/services/RestCallService.jsx | 42 +- src/main/frontend/src/setupTests.js | 2 +- .../cherry/admin/AdminRestController.java | 93 ++-- .../cherry/admin/RunnerRestController.java | 61 ++- .../cherry/content/ContentRestController.java | 144 +++++- .../cherry/db/entity/JarStorageEntity.java | 13 + .../cherry/db/entity/OperationEntity.java | 2 +- .../db/entity/RunnerDefinitionEntity.java | 22 + .../definition/AbstractConnectorInput.java | 4 +- .../cherry/definition/AbstractRunner.java | 193 +++++--- .../cherry/definition/AbstractWorker.java | 10 +- .../definition/RunnerDecorationTemplate.java | 94 ++-- .../cherry/definition/RunnerParameter.java | 119 ++++- .../cherry/definition/SdkRunnerConnector.java | 61 --- .../connector/SdkRunnerCherryConnector.java | 214 +++++++++ .../connector/SdkRunnerConnector.java | 86 ++++ .../definition/connector/SdkRunnerWorker.java | 79 ++++ .../files/LoadFileFromDiskWorker.java | 18 +- .../embeddedrunner/files/PurgeFileWorker.java | 4 +- .../operations/SetVariableWorker.java | 25 +- .../embeddedrunner/ping/PingIntRunner.java | 7 + .../ping/basicworker/BasicPingWorker.java | 102 +++++ .../ping/connector/PingConnector.java | 7 +- .../ping/connector/PingConnectorInput.java | 8 +- .../ping/connector/PingConnectorOutput.java | 8 + .../objectconnector/PingObjectConnector.java | 12 +- ...{PingWorker.java => PingCherryWorker.java} | 47 +- .../cherry/exception/OperationException.java | 4 + .../cherry/runner/JobRunnerFactory.java | 167 +++++-- .../camunda/cherry/runner/LogOperation.java | 18 +- .../cherry/runner/RunnerAdminOperation.java | 73 +++ .../runner/RunnerClassLoaderFactory.java | 127 ++++++ .../cherry/runner/RunnerEmbeddedFactory.java | 81 +++- .../camunda/cherry/runner/RunnerFactory.java | 294 ++++++++++-- .../cherry/runner/RunnerLightDefinition.java | 18 +- .../cherry/runner/RunnerUploadFactory.java | 418 +++++++++++------- .../camunda/cherry/runner/StorageRunner.java | 79 +++- .../runner/handler/ActivatedJobContext.java | 76 ++++ .../handler}/CherryConnectorJobHandler.java | 107 +++-- .../handler/CherryWorkerJobHandler.java | 72 +++ .../runner/handler/JobHandlerContext.java | 98 ++++ .../handler/SuperConnectorJobHandler.java | 61 +++ .../io/camunda/cherry/runtime/CherryMain.java | 4 +- .../cherry/runtime/HistoryFactory.java | 2 +- .../cherry/runtime/SecretProvider.java | 10 + .../cherry/runtime/ValidationProvider.java | 11 + .../io/camunda/cherry/store/StoreService.java | 6 +- .../cherry/zeebe/ZeebeConfiguration.java | 142 ++++-- .../camunda/cherry/zeebe/ZeebeContainer.java | 97 +++- .../cherrytemplate/CherryConnector.java | 81 ++++ .../connector/cherrytemplate/CherryInput.java | 45 ++ .../cherrytemplate/CherryOutput.java | 14 + .../connector/cherrytemplate/README.md | 28 ++ src/main/resources/application-postgres.yaml | 65 ++- src/main/resources/application.yaml | 122 ++--- src/main/resources/banner.txt | 7 + 83 files changed, 3654 insertions(+), 932 deletions(-) create mode 100644 doc/CherryArchitecture.md create mode 100644 doc/todo.md create mode 100644 src/main/frontend/src/dashboard/Monitoring.jsx delete mode 100644 src/main/java/io/camunda/cherry/definition/SdkRunnerConnector.java create mode 100644 src/main/java/io/camunda/cherry/definition/connector/SdkRunnerCherryConnector.java create mode 100644 src/main/java/io/camunda/cherry/definition/connector/SdkRunnerConnector.java create mode 100644 src/main/java/io/camunda/cherry/definition/connector/SdkRunnerWorker.java create mode 100644 src/main/java/io/camunda/cherry/embeddedrunner/ping/PingIntRunner.java create mode 100644 src/main/java/io/camunda/cherry/embeddedrunner/ping/basicworker/BasicPingWorker.java rename src/main/java/io/camunda/cherry/embeddedrunner/ping/worker/{PingWorker.java => PingCherryWorker.java} (55%) create mode 100644 src/main/java/io/camunda/cherry/runner/RunnerAdminOperation.java create mode 100644 src/main/java/io/camunda/cherry/runner/RunnerClassLoaderFactory.java create mode 100644 src/main/java/io/camunda/cherry/runner/handler/ActivatedJobContext.java rename src/main/java/io/camunda/cherry/{definition => runner/handler}/CherryConnectorJobHandler.java (50%) create mode 100644 src/main/java/io/camunda/cherry/runner/handler/CherryWorkerJobHandler.java create mode 100644 src/main/java/io/camunda/cherry/runner/handler/JobHandlerContext.java create mode 100644 src/main/java/io/camunda/cherry/runner/handler/SuperConnectorJobHandler.java create mode 100644 src/main/java/io/camunda/cherry/runtime/ValidationProvider.java create mode 100644 src/main/java/io/camunda/connector/cherrytemplate/CherryConnector.java create mode 100644 src/main/java/io/camunda/connector/cherrytemplate/CherryInput.java create mode 100644 src/main/java/io/camunda/connector/cherrytemplate/CherryOutput.java create mode 100644 src/main/java/io/camunda/connector/cherrytemplate/README.md create mode 100644 src/main/resources/banner.txt diff --git a/README.md b/README.md index ed99998..5438e53 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,11 @@ Docker image is then available in the package # Build The project is configured to publish automatically to maven central the JAR file, and to docker package a Docker image +If you want to build a local maven image, use + +````yaml +mvn spring-boot:build-image +```` ## Maven Central repository See .github/workflows/mvn-release.yml diff --git a/doc/CherryArchitecture.md b/doc/CherryArchitecture.md new file mode 100644 index 0000000..f620fac --- /dev/null +++ b/doc/CherryArchitecture.md @@ -0,0 +1,10 @@ +# Cherry architecture + +## bean zeebeClient + +Some connector needs to access the zeebeClient object. +Using the library io.camunda.spring.spring-boot-starter-camunda onboard a bean zeebeClient. +This client is created directly from the application.yaml + +But in the configuration, there is multiple way to declare the zeebeClient: via a list, or from different other way. +ZeebeContainer know the client client in use, so this class redefine the bean with a @Primary option. diff --git a/doc/DeveloperGuide/README.md b/doc/DeveloperGuide/README.md index da420fe..84674c6 100644 --- a/doc/DeveloperGuide/README.md +++ b/doc/DeveloperGuide/README.md @@ -128,7 +128,6 @@ A parameter can be a list of choice, and the designer will choose one in the dro ### List of BPMN Errors -![Bpmn Errors documentation](BpmnErrorsDocumentation.png?raw=true) Give the list of BPMN Errors that the connector/worker can throw. This list will be available in the documentation. The Element-Template will contain the FEEL expression to transform a `ConnectorException` to a BPMN Error. @@ -143,9 +142,9 @@ The worker has only one class, and get directly the information from the context Declare in the constructor the different input. (see `src/main/java/io/camunda/cherry/embeddedrunner/ping/worker/PingWorker.java`) -`````java +````` @Component -public class PingWorker extends AbstractWorker implements IntFrameworkRunner { +public class PingWorker extends AbstractWorker implements IntruntimeRunner { public PingWorker() { super("c-ping", @@ -191,7 +190,7 @@ The recommendation is to declare a constant for each Input to ensure consistency public class PingConnectorInput extends AbstractConnectorInput { // see -// https://docs.camunda.io/docs/components/integration-framework/connectors/custom-built-connectors/connector-sdk/#validation +// https://docs.camunda.io/docs/components/integration-runtime/connectors/custom-built-connectors/connector-sdk/#validation @NotEmpty protected final static String INPUT_MESSAGE="message"; @@ -205,7 +204,7 @@ private boolean throwErrorPlease; ````` Then the value can be used in the declaration -`````java +````` @Component @OutboundConnector(name = PingConnector.TYPE_PINGCONNECTOR, inputVariables = { PingConnectorInput.INPUT_MESSAGE, @@ -217,7 +216,7 @@ public class PingConnector extends AbstractConnector implements OutboundConnecto In the constructor, reference the Input class and the Output class. The Cherry runtime will introspect these class to create the documentation and the Element-template -`````java +````` public PingConnector() { super(TYPE_PINGCONNECTOR, @@ -227,9 +226,9 @@ public PingConnector() { ````` -Doing this ways, the Cherry framework will not find the documentation to build the documentation or the Element-template. +Doing this way, the Cherry runtime will not find the documentation to build the documentation or the Element-template. -Do do that, the Input object need to extends the `AbstractConnectorInput` class +Do do that, the Input object need to extend the `AbstractConnectorInput` class ```` public class PingConnectorInput extends AbstractConnectorInput { @@ -315,7 +314,7 @@ Then, you have to specify the list of outputs in the method `getOutputParameters **You must create a getter for each member, starting with a lower case** For example, for the object -````java +```` private long internalTimeStampMS; ```` @@ -402,7 +401,7 @@ You can override different method: } ```` -The logo must be a SVG image. +The logo must be an SVG image. ## Execution Then here you are: the execute method! @@ -504,3 +503,14 @@ Start the Cherry runtime. Your connector appears in the dashboard, and the Eleme You can download the complete collection to save for the desktop Modeler. Or you can access the definition of one connector/worker to create a connector template in the Web Modeler. One connector template must be created one by one. + +# Integrate an external connector +You have an external connector, and when you will upload it in Cherry runtime, you want to take advantages of all functions, displaying it with logo, display the list of Inputs, Outputs, BPMNError. + +You want to generate the element-template by the Cherry runtime, with advance controles. + +It is possible to do that without embedded any Cherry Library. You will just need to add some methods in your connector, then the runtime will be able to access it. +For example, the Cherry runtime will try to access a method `getLogo()` to read the logo. + +To be sure to not miss any function, check interfaces CherryConnector, CherryInput, CherryOutput stored +under `io.camunda.connector.cherrytemplate`. Copy the package in your connector, and follow the README.MD diff --git a/doc/todo.md b/doc/todo.md new file mode 100644 index 0000000..e5fd6a0 --- /dev/null +++ b/doc/todo.md @@ -0,0 +1,59 @@ +# todo + +Main new feature are registered via the github issue, but here are additionnal (easy to write) + +# Element Template + +## version +The element template accept a version number +in the element template and in the Cherry + + +## handle message + +The XML must contain this +```` + +```` +to let the connector catch the throw event. +How to add it in the element-template? + +This should be the correct information, but does not work + +```` + +{ + "type": "Hidden", + "generatedValue": { + "type": "uuid" + }, + "binding": { + "type": "bpmn:Message#property", + "name": "name" + } + } + +```` + +# Inbound connector +Cherry handle Inbound connector + +# Upload connector JAR + +## PVC +Documentation: via a PVC, upload the JAR and Cherry load it at the beginning. + +## Manual upload +Create a load from the UI + +## marketPlace +jar can be upload diretly from the marketplace + +## Google Drive +A google drive access is provided in the configuration (or via the UI), and Cherry upload JAR in this google drive + +## Bucket access +A Bucket access is provided in the configuration (or via the UI), and Cherry upload JAR in this bucket + +## Git Repository +A Git repository is provided in the configuration (or via the UI), and Cherry upload JAR in this Gir Repo (in the release) diff --git a/element-templates/Ping connector.json b/element-templates/Ping connector.json index 510be69..88bc9c2 100644 --- a/element-templates/Ping connector.json +++ b/element-templates/Ping connector.json @@ -206,7 +206,7 @@ "property": "ipAddress_optional", "equals": "true" }, - "description": "Returm the IpAddress", + "description": "Return the IpAddress", "binding": { "type": "zeebe:output", "source": "\u003d result.ipAddress" @@ -220,7 +220,7 @@ "group": "Output" }, { - "description": "Returm parameters", + "description": "Return parameters", "binding": { "type": "zeebe:input", "name": "parameters_optional" diff --git a/pom.xml b/pom.xml index c04d855..da3765a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,25 +4,30 @@ io.camunda.community zeebe-cherry-runtime - 3.0.4 + 3.1.1 17 ${java.version} ${java.version} - - 8.2.0 - 8.2.4 - 0.9.0 - 0.9.0 + + 8.3.0 + 8.3.3 + 8.3.1 + 8.3.1 - 1.1.0 + 1.2.0 + + + 2.7.4 5.9.1 2.0.3 - 2.7.4 + + 2.0.4 + @@ -49,17 +54,28 @@ - - io.camunda.spring - spring-boot-starter-camunda - ${zeebe.version} - - - io.camunda - zeebe-client-java - ${zeebe-client.version} - + + + + + + io.camunda.spring + spring-boot-starter-camunda + ${zeebe.version} + + + + + io.camunda + zeebe-client-java + ${zeebe-client.version} + @@ -68,11 +84,18 @@ connector-core ${connector-core.version} + + io.camunda.connector + connector-runtime-core + ${connector-core.version} + io.camunda.connector connector-validation ${connector-validation.version} + + @@ -152,6 +175,61 @@ test + + + + + + + + fr.opensagres.xdocreport + fr.opensagres.xdocreport.document + ${opensagres.version} + + + fr.opensagres.xdocreport + fr.opensagres.xdocreport.document.docx + ${opensagres.version} + + + fr.opensagres.xdocreport + fr.opensagres.xdocreport.document.odt + ${opensagres.version} + + + fr.opensagres.xdocreport + fr.opensagres.xdocreport.converter.odt.odfdom + ${opensagres.version} + + + xml-apis + xml-apis + + + + + fr.opensagres.xdocreport + xdocreport + ${opensagres.version} + compile + + + fr.opensagres.xdocreport + fr.opensagres.xdocreport.converter + ${opensagres.version} + + + fr.opensagres.xdocreport + fr.opensagres.xdocreport.template.velocity + ${opensagres.version} + + + fr.opensagres.xdocreport + fr.opensagres.xdocreport.converter.docx.xwpf + ${opensagres.version} + + + @@ -234,10 +312,11 @@ + org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.13.0 17 17 diff --git a/src/main/frontend/src/App.js b/src/main/frontend/src/App.js index 3784575..53ceec4 100644 --- a/src/main/frontend/src/App.js +++ b/src/main/frontend/src/App.js @@ -5,7 +5,7 @@ function App() { return (
- logo + logo

Edit src/App.js and save to reload.

diff --git a/src/main/frontend/src/App.test.js b/src/main/frontend/src/App.test.js index 1f03afe..ed340df 100644 --- a/src/main/frontend/src/App.test.js +++ b/src/main/frontend/src/App.test.js @@ -1,8 +1,8 @@ -import { render, screen } from '@testing-library/react'; +import {render, screen} from '@testing-library/react'; import App from './App'; test('renders learn react link', () => { - render(); + render(); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); }); diff --git a/src/main/frontend/src/CherryApp.jsx b/src/main/frontend/src/CherryApp.jsx index ff87ec4..f560701 100644 --- a/src/main/frontend/src/CherryApp.jsx +++ b/src/main/frontend/src/CherryApp.jsx @@ -28,7 +28,7 @@ const FRAME_NAME = { CONTENT: "Content", PARAMETERS: "Parameters", STORE: "Store", - OPERATIONLOG : "OperationLog" + OPERATIONLOG: "OperationLog" } diff --git a/src/main/frontend/src/component/ControllerPage.jsx b/src/main/frontend/src/component/ControllerPage.jsx index 977ce46..18954e3 100644 --- a/src/main/frontend/src/component/ControllerPage.jsx +++ b/src/main/frontend/src/component/ControllerPage.jsx @@ -40,7 +40,12 @@ class ControllerPage extends React.Component { return (
{this.state.loading && -
Loading
} + + + + + +
Loading
} {this.state.errorMessage &&
diff --git a/src/main/frontend/src/content/Content.jsx b/src/main/frontend/src/content/Content.jsx index 82abd74..d9ebb5b 100644 --- a/src/main/frontend/src/content/Content.jsx +++ b/src/main/frontend/src/content/Content.jsx @@ -6,8 +6,8 @@ // // ----------------------------------------------------------- -import React from 'react'; -import {Button} from "carbon-components-react"; +import React,{ createRef } from 'react'; +import {Button, FileUploader, Tag} from "carbon-components-react"; import {ArrowRepeat, ConeStriped} from "react-bootstrap-icons"; import ControllerPage from "../component/ControllerPage"; import RestCallService from "../services/RestCallService"; @@ -17,10 +17,13 @@ class Content extends React.Component { constructor(_props) { super(); + this.fileUploaderRef = createRef(); + this.state = { content: [], + files:[], display: {loading: false}, - status:"" + status: "" }; } @@ -38,7 +41,10 @@ class Content extends React.Component {
@@ -59,9 +65,9 @@ class Content extends React.Component { Name Used by - Loaded time + Loaded Log - + @@ -71,10 +77,18 @@ class Content extends React.Component { {content.name} - - {content.usedby.map((usedby, _indexcontent) => -
{usedby.name} {usedby.collection}
) - } + + {content.usedby.map((usedby, _indexcontent) => +
{usedby.name} {usedby.collectionName}
+ {usedby.activeRunner && + + } + {!usedby.activeRunner && + + } +
+ ) + } {content.loadedtime} @@ -84,11 +98,10 @@ class Content extends React.Component { @@ -99,6 +112,40 @@ class Content extends React.Component {
+
+
+ this.handleFileChange(event)} + multiple + iconDescription="Clear file" + disabled={this.state.display.loading || this.state.files.size == 0} + /> + +
+ Upload a Connector Jar directly from your disk to Cherry. It will be analysed and all workers/connectors + detected started. + + {this.state.statusUploadFailed &&
+ {this.state.statusUploadFailed} +
+ } + {this.state.statusUploadSuccess &&
+ {this.state.statusUploadSuccess} +
} +
+
) } @@ -107,6 +154,7 @@ class Content extends React.Component { getStyleRow(_content) { return {}; } + refreshListContent() { let uri = 'cherry/api/content/list?'; console.log("Content.refreshListContent http[" + uri + "]"); @@ -120,7 +168,7 @@ class Content extends React.Component { refreshListContentCallback(httpPayload) { this.setDisplayProperty("loading", false); if (httpPayload.isError()) { - console.log("Content.refreshListContentCallback: error " + httpPayload.getError()); + console.log("Content.refreshListContentCallback: error: " + httpPayload.getError()); this.setState({status: httpPayload.getError()}); } else { this.setState({content: httpPayload.getData()}); @@ -138,6 +186,74 @@ class Content extends React.Component { displayObject[propertyName] = propertyValue; this.setState({display: displayObject}); } + + deleteStorageEntityId(storageentityid) { + this.refreshStatusOnPage(); + console.log("Content.deleteStorageEntityId" + storageentityid); + const userConfirmed = window.confirm("Are you sure you want to delete this Jar?"); + if (userConfirmed) { + this.setDisplayProperty("loading", true); + this.setState({labelBtnStop: "Deleting...", status: ""}); + var restCallService = RestCallService.getInstance(); + restCallService.putJson('cherry/api/content/delete?storageentityid=' + storageentityid, {}, this, this.operationDeleteCallback); + } + } + + operationDeleteCallback(httpResponse) { + this.setDisplayProperty("loading", false); + + if (httpResponse.isError()) { + console.log("deleteStorageEntityId.operationDeleteCallback: error " + httpResponse.getError()); + this.setState({status: httpResponse.getError()}); + } else { + } + this.refreshListContent(); + } + + handleFileChange(event) { + this.refreshStatusOnPage(); + const fileList = event.target.files; + this.setState({files: fileList}); // Use spread operator to create a new array + }; + + + loadJar(event) { + console.log("Load Jar ", this.state.files); + this.refreshStatusOnPage(); + let restCallService = RestCallService.getInstance(); + + const formData = new FormData(); + Array.from(this.state.files).forEach((file, index) => { + formData.append(`File`, file); + }); + /* formData.append("File", this.state.files[0]); */ + this.setDisplayProperty("loading", true); + + restCallService.postUpload('cherry/api/content/add?', formData, this, this.operationUploadJarCallback); + + + // dispatch(connectorService.uploadJar(event.target.files[0])); + } + + operationUploadJarCallback(httpResponse) { + this.setDisplayProperty("loading", false); + + if (httpResponse.isError()) { + console.log("operationUploadJar.operationDeleteCallback: error " + httpResponse.getError()); + this.setState({statusUploadFailed: httpResponse.getError()}); + } else { + // Clear the file input field using JavaScript + if (this.fileUploaderRef.current) { + this.fileUploaderRef.current.clearFiles(); + } + this.setState({'files': [], statusUploadSuccess: 'Jar uploaded with success'}); + } + this.refreshListContent(); + } + + refreshStatusOnPage() { + this.setState({statusUploadFailed: '', statusUploadSuccess: '', status: ''}); + } } export default Content; \ No newline at end of file diff --git a/src/main/frontend/src/dashboard/Dashboard.jsx b/src/main/frontend/src/dashboard/Dashboard.jsx index 5abf71c..3370937 100644 --- a/src/main/frontend/src/dashboard/Dashboard.jsx +++ b/src/main/frontend/src/dashboard/Dashboard.jsx @@ -32,13 +32,13 @@ class Dashboard extends React.Component { orderBy: "nameAsc", period: "ONEDAY", showActive: true, - showInactive: true, + showInactive: false, showWorker: true, showConnector: true, showOnlyError: false, showOnlyOverThreshold: false, showFrameworkRunner: true, - filterSearch:"" + filterSearch: "" }, @@ -95,7 +95,7 @@ class Dashboard extends React.Component { style={{marginLeft: "10px", fontSize: "10px"}} disabled={this.state.display.loading} onClick={() => this.setToggleFilter("showOnlyError")}> - Only Errors + Only Errors