From f9b4429c303c249abc56a21cfc19f2f15b33c7c8 Mon Sep 17 00:00:00 2001 From: Pavel Kotelevsky <38818382+chillleader@users.noreply.github.com> Date: Mon, 9 Jan 2023 15:29:21 +0100 Subject: [PATCH] feat(monorepo): add archetype for quick creation of new connectors (#166) --- add_new_connector.sh | 23 ++++ connector-archetype-internal/pom.xml | 33 ++++++ .../META-INF/maven/archetype-metadata.xml | 47 ++++++++ .../resources/archetype-resources/LICENSE.txt | 5 + .../resources/archetype-resources/README.md | 45 ++++++++ .../element-templates/template-connector.json | 106 ++++++++++++++++++ .../resources/archetype-resources/pom.xml | 31 +++++ .../src/main/java/Authentication.java | 63 +++++++++++ .../src/main/java/MyConnectorFunction.java | 54 +++++++++ .../src/main/java/MyConnectorRequest.java | 68 +++++++++++ .../src/main/java/MyConnectorResult.java | 55 +++++++++ ...tor.api.outbound.OutboundConnectorFunction | 1 + connectors/README.md | 23 ++++ pom.xml | 1 + 14 files changed, 555 insertions(+) create mode 100755 add_new_connector.sh create mode 100644 connector-archetype-internal/pom.xml create mode 100644 connector-archetype-internal/src/main/resources/META-INF/maven/archetype-metadata.xml create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/LICENSE.txt create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/README.md create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/element-templates/template-connector.json create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/pom.xml create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/Authentication.java create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorFunction.java create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorRequest.java create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorResult.java create mode 100644 connector-archetype-internal/src/main/resources/archetype-resources/src/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction diff --git a/add_new_connector.sh b/add_new_connector.sh new file mode 100755 index 0000000000..759e74e817 --- /dev/null +++ b/add_new_connector.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +CONNECTORS_DIR='connectors' + +CONNECTOR_NAME=${1} +ARTIFACT_NAME="connector-${CONNECTOR_NAME}" + +VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) + +# Using old (3.1.0) version due to a bug in the newer one (3.2.1) +# https://issues.apache.org/jira/browse/ARCHETYPE-584 +mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.0:generate \ + -DarchetypeGroupId=io.camunda.connector \ + -DarchetypeArtifactId=connector-archetype-internal \ + -DarchetypeVersion=${VERSION} \ + -DinteractiveMode=false \ + -DconnectorName=${CONNECTOR_NAME} \ + -DoutputDirectory=${CONNECTORS_DIR} + +# Rename directory to follow convention +mv "${CONNECTORS_DIR}/${ARTIFACT_NAME}" "${CONNECTORS_DIR}/${CONNECTOR_NAME}" +sed "s/${ARTIFACT_NAME}/${CONNECTOR_NAME}/" ${CONNECTORS_DIR}/pom.xml \ + | diff -p ${CONNECTORS_DIR}/pom.xml /dev/stdin | patch diff --git a/connector-archetype-internal/pom.xml b/connector-archetype-internal/pom.xml new file mode 100644 index 0000000000..a0986079b2 --- /dev/null +++ b/connector-archetype-internal/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + + io.camunda.connector + connectors-bundle-parent + 0.14.3-SNAPSHOT + ../pom.xml + + + connector-archetype-internal + maven-archetype + + + 3.2.1 + + false + + + + + + org.apache.maven.archetype + archetype-packaging + ${archetype-packaging.version} + + + + + \ No newline at end of file diff --git a/connector-archetype-internal/src/main/resources/META-INF/maven/archetype-metadata.xml b/connector-archetype-internal/src/main/resources/META-INF/maven/archetype-metadata.xml new file mode 100644 index 0000000000..c43159c4e2 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -0,0 +1,47 @@ + + + + + src/main/java + + + src/test/java + + + src/main/resources + + + element-templates + + + + + README.md + LICENSE.txt + + + + + + + + ${connectorName} + + + connector-${connectorName} + + + io.camunda.connector + + + ${version} + + + ${groupId}.${connectorName} + + + \ No newline at end of file diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/LICENSE.txt b/connector-archetype-internal/src/main/resources/archetype-resources/LICENSE.txt new file mode 100644 index 0000000000..faff62873e --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/LICENSE.txt @@ -0,0 +1,5 @@ +Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under one or more contributor license agreements and licensed to you under a proprietary license. +You may not use this file except in compliance with the proprietary license. +The proprietary license can be either the Camunda Platform Self-Managed Free Edition license (available on Camunda’s website) or the Camunda Platform Self-Managed Enterprise Edition license (a copy you obtain when you contact Camunda). +The Camunda Platform Self-Managed Free Edition comes for free but only allows for usage of the software (file) in non-production environments. +If you want to use the software (file) in production, you need to purchase the Camunda Platform Self-Managed Enterprise Edition. diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/README.md b/connector-archetype-internal/src/main/resources/archetype-resources/README.md new file mode 100644 index 0000000000..519ab16282 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/README.md @@ -0,0 +1,45 @@ +# Camunda Connector Template + +## Build + +```bash +mvn clean package +``` + +## API + +### Input + +```json +{ + "myProperty": "....." +} +``` + +### Output + +```json +{ + "result": { + "myProperty": "....." + } +} +``` + +### Error codes + +| Code | Description | +| - | - | +| FAIL | Message starts with 'fail' (ignoring case) | + +## Test locally + +Run unit tests + +```bash +mvn clean verify +``` + +## Element Template + +The element templates can be found in the [element-templates](element-templates) directory. diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/element-templates/template-connector.json b/connector-archetype-internal/src/main/resources/archetype-resources/element-templates/template-connector.json new file mode 100644 index 0000000000..8273efea12 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/element-templates/template-connector.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name": "Template: Some Function", + "id": "io.camunda.connectors.Template.v1", + "description": "Describe this connector", + "version": 1, + "documentationRef": "https://docs.camunda.io/docs/components/modeler/web-modeler/connectors/available-connectors/template/", + "icon": { + "contents": "data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2218%22%20width%3D%2218%22%20viewBox%3D%220%200%2010%2010%22%20shape-rendering%3D%22geometricPrecision%22%3E%3Ctitle%3ESlack%3C%2Ftitle%3E%3Cg%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M0%2C0%20L0%2C10%20L10%2C10%20L10%2C0%20z%22%20fill%3D%22%23ecb12f%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E" + }, + "category": { + "id": "connectors", + "name": "Connectors" + }, + "appliesTo": [ + "bpmn:Task" + ], + "elementType": { + "value": "bpmn:ServiceTask" + }, + "groups": [ + { + "id": "authentication", + "label": "Authentication" + }, + { + "id": "compose", + "label": "Compose" + }, + { + "id": "output", + "label": "Output Mapping" + }, + { + "id": "errors", + "label": "Error Handling" + } + ], + "properties": [ + { + "type": "Hidden", + "value": "io.camunda:template:1", + "binding": { + "type": "zeebe:taskDefinition:type" + } + }, + { + "label": "OAuth Token", + "group": "authentication", + "type": "String", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "token" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Message", + "group": "compose", + "type": "Text", + "feel": "optional", + "binding": { + "type": "zeebe:input", + "name": "data.message" + }, + "constraints": { + "notEmpty": true + } + }, + { + "label": "Result Variable", + "description": "Name of variable to store the response in", + "group": "output", + "type": "String", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultVariable" + } + }, + { + "label": "Result Expression", + "description": "Expression to map the response into process variables", + "group": "output", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:taskHeader", + "key": "resultExpression" + } + }, + { + "label": "Error Expression", + "description": "Expression to handle errors. Details in the documentation.", + "group": "errors", + "type": "Text", + "feel": "required", + "binding": { + "type": "zeebe:taskHeader", + "key": "errorExpression" + } + } + ] +} diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/pom.xml b/connector-archetype-internal/src/main/resources/archetype-resources/pom.xml new file mode 100644 index 0000000000..025a656a07 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + + io.camunda.connector + connector-function-parent + ${archetypeVersion} + ../pom.xml + + + ${artifactId} + ${connectorName} cnnector for Camunda 8 + ${artifactId} + jar + + + + Camunda Platform Self-Managed Free Edition license + https://camunda.com/legal/terms/cloud-terms-and-conditions/camunda-cloud-self-managed-free-edition-terms/ + + + Camunda Platform Self-Managed Enterprise Edition license + + + + + + + diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/Authentication.java b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/Authentication.java new file mode 100644 index 0000000000..ac504e3102 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/Authentication.java @@ -0,0 +1,63 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import io.camunda.connector.api.annotation.Secret; +import java.util.Objects; +import javax.validation.constraints.NotEmpty; + +public class Authentication { + + @NotEmpty private String user; + + @NotEmpty @Secret private String token; + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + @Override + public int hashCode() { + return Objects.hash(token, user); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Authentication other = (Authentication) obj; + return Objects.equals(token, other.token) && Objects.equals(user, other.user); + } + + @Override + public String toString() { + return "Authentication [user=" + user + ", token=" + token + "]"; + } +} diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorFunction.java b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorFunction.java new file mode 100644 index 0000000000..e8426b47e3 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorFunction.java @@ -0,0 +1,54 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import io.camunda.connector.api.annotation.OutboundConnector; +import io.camunda.connector.api.error.ConnectorException; +import io.camunda.connector.api.outbound.OutboundConnectorContext; +import io.camunda.connector.api.outbound.OutboundConnectorFunction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@OutboundConnector( + name = "MYCONNECTOR", + inputVariables = {"myProperty", "authentication"}, + type = "io.camunda:my-connector:1") +public class MyConnectorFunction implements OutboundConnectorFunction { + + private static final Logger LOGGER = LoggerFactory.getLogger(MyConnectorFunction.class); + + @Override + public Object execute(OutboundConnectorContext context) throws Exception { + var connectorRequest = context.getVariablesAsType(MyConnectorRequest.class); + context.validate(connectorRequest); + context.replaceSecrets(connectorRequest); + + return executeConnector(connectorRequest); + } + + private MyConnectorResult executeConnector(final MyConnectorRequest connectorRequest) { + // TODO: implement connector logic + LOGGER.info("Executing my connector with request {}", connectorRequest); + String myProperty = connectorRequest.getMyProperty(); + if (myProperty != null && myProperty.toLowerCase().startsWith("fail")) { + throw new ConnectorException("FAIL", "My property started with 'fail', was: " + myProperty); + } + var result = new MyConnectorResult(); + result.setMyProperty(myProperty); + return result; + } +} diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorRequest.java b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorRequest.java new file mode 100644 index 0000000000..a095e2f2a4 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorRequest.java @@ -0,0 +1,68 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import io.camunda.connector.api.annotation.Secret; +import java.util.Objects; +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +public class MyConnectorRequest { + + @NotEmpty private String myProperty; + + @Valid @NotNull @Secret private Authentication authentication; + + // TODO: add request properties + + public String getMyProperty() { + return myProperty; + } + + public void setMyProperty(final String myProperty) { + this.myProperty = myProperty; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MyConnectorRequest that = (MyConnectorRequest) o; + return myProperty.equals(that.myProperty) && authentication.equals(that.authentication); + } + + @Override + public int hashCode() { + return Objects.hash(myProperty, authentication); + } + + @Override + public String toString() { + return "MyConnectorRequest{" + + "myProperty='" + + myProperty + + '\'' + + ", authentication=" + + authentication + + '}'; + } +} diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorResult.java b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorResult.java new file mode 100644 index 0000000000..d8e8a83fc9 --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/src/main/java/MyConnectorResult.java @@ -0,0 +1,55 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. Camunda licenses this file to you under the Apache License, + * Version 2.0; you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import java.util.Objects; + +public class MyConnectorResult { + + // TODO: define connector result properties, which are returned to the process engine + private String myProperty; + + public String getMyProperty() { + return myProperty; + } + + public void setMyProperty(String myProperty) { + this.myProperty = myProperty; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final MyConnectorResult that = (MyConnectorResult) o; + return Objects.equals(myProperty, that.myProperty); + } + + @Override + public int hashCode() { + return Objects.hash(myProperty); + } + + @Override + public String toString() { + return "MyConnectorResult{" + "myProperty='" + myProperty + '\'' + '}'; + } +} diff --git a/connector-archetype-internal/src/main/resources/archetype-resources/src/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction b/connector-archetype-internal/src/main/resources/archetype-resources/src/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction new file mode 100644 index 0000000000..60900aa7ce --- /dev/null +++ b/connector-archetype-internal/src/main/resources/archetype-resources/src/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction @@ -0,0 +1 @@ +io.camunda.connector.MyConnectorFunction \ No newline at end of file diff --git a/connectors/README.md b/connectors/README.md index e721d06895..32072326c9 100644 --- a/connectors/README.md +++ b/connectors/README.md @@ -38,3 +38,26 @@ is run as a Google Cloud Function. The element templates can be found in the `element-templates` directory of each connector. Example: [aws-lambda/element-templates](aws-lambda/element-templates/aws-lambda-connector.json) + +## Development guidelines + +### Create a new Connector + +To create a new Connector, simply use the [script](../add_new_connector.sh) or generate a new +project from the [Maven archetype](../connector-archetype-internal). + +Execute from the repository root directory: +```shell +./add_new_connector.sh ${YOUR_CONNECTOR_NAME} +``` +Substitute `${YOUR_CONNECTOR_NAME}` with the name of your new Connector. +**Please provide the name in a short format:** e.g. use simply `slack`, **not** `connector-slack`. + +The script will create a Maven sub-module in the `connectors` directory. You will likely +need to import the project in your IDE manually. + +### Add new Connector to the bundle + +As a next step, please include your Connector in the connectors bundle. +- Add it to the `dependencyManagement` section of [bundle parent POM](../bundle/mvn/pom.xml) +- Add it to the `dependencies` section of [default bundle POM](../bundle/mvn/default-bundle/pom.xml) diff --git a/pom.xml b/pom.xml index 070146fa15..2e465d636f 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,7 @@ connectors bundle/mvn + connector-archetype-internal