From 5ab8ecb748266e9d402d779453196fa7e1a4f3cb Mon Sep 17 00:00:00 2001 From: Mathias Vandaele Date: Mon, 23 Sep 2024 16:01:29 +0200 Subject: [PATCH] feat(email connector): implement new email connector, Outbound SMTP, IMAP and POP3, Inbound IMAP (#3132) * feat(email connector): first checkpoint on email connector * feat(email connector): first checkpoint on email connector 2 * feat(email connector): first checkpoint on email connector 3 * feat(email connector): first checkpoint on email connector 4 * feat(email connector): first checkpoint on email connector 5 * feat(email connector): first checkpoint on email connector 5 * feat(email connector): first checkpoint on email connector 6 * feat(email connector): first checkpoint on email connector 7 * feat(email connector): put everything inside a directory * feat(email connector): checkpoint on email connector 8 * feat(email connector): checkpoint on email connector 9 * feat(email connector): checkpoint on email connector 10 * feat(email connector): checkpoint on email connector 11 * feat(email connector): checkpoint on email connector 12 * feat(email connector): checkpoint on email connector 13 * feat(email connector): checkpoint on email connector 15 * feat(email connector): checkpoint on email connector 16 * feat(email connector): remove local application.properties * feat(email connector): tests addition * feat(email connector): applies correction after PR reviews * feat(email connector): applies correction after PR reviews * feat(email connector): modify the attachment management * feat(email connector): inbound checkpoint * feat(email connector): inbound inbound refactor and labels refinment * feat(email connector): inbound inbound refactor and labels refinment 2 * feat(email connector): inbound inbound refactor and labels refinment 3 --- bundle/default-bundle/pom.xml | 4 + bundle/pom.xml | 5 + .../json/ConnectorsObjectMapperSupplier.java | 4 +- .../email-inbound-connector-boundary.json | 419 ++++++ .../email-inbound-connector-intermediate.json | 419 ++++++ .../email-message-start-event-connector.json | 447 +++++++ .../email-outbound-connector.json | 1184 ++++++++++++++++ ...ail-inbound-connector-boundary-hybrid.json | 424 ++++++ ...inbound-connector-intermediate-hybrid.json | 424 ++++++ .../email-outbound-connector-hybrid.json | 1189 +++++++++++++++++ ...-message-start-event-connector-hybrid.json | 452 +++++++ connectors/email/pom.xml | 87 ++ .../email/authentication/Authentication.java | 24 + .../authentication/SimpleAuthentication.java | 33 + .../email/client/EmailActionExecutor.java | 13 + .../connector/email/client/EmailListener.java | 15 + .../connector/email/client/jakarta/Email.java | 21 + .../email/client/jakarta/EmailAttachment.java | 11 + .../email/client/jakarta/EmailBody.java | 48 + .../jakarta/JakartaEmailActionExecutor.java | 340 +++++ .../client/jakarta/JakartaEmailListener.java | 213 +++ .../email/client/jakarta/JakartaUtils.java | 322 +++++ .../connector/email/config/Configuration.java | 9 + .../email/config/CryptographicProtocol.java | 13 + .../connector/email/config/ImapConfig.java | 56 + .../connector/email/config/Pop3Config.java | 56 + .../connector/email/config/SmtpConfig.java | 56 + .../inbound/EmailConnectorExecutable.java | 75 ++ .../EmailInboundConnectorProperties.java | 18 + .../inbound/model/EmailListenerConfig.java | 87 ++ .../email/inbound/model/HandlingStrategy.java | 14 + .../inbound/model/InitialPollingConfig.java | 13 + .../outbound/EmailConnectorFunction.java | 68 + .../email/outbound/model/EmailRequest.java | 37 + .../email/outbound/protocols/Imap.java | 48 + .../email/outbound/protocols/Pop3.java | 47 + .../email/outbound/protocols/Protocol.java | 24 + .../email/outbound/protocols/Smtp.java | 50 + .../outbound/protocols/actions/Action.java | 9 + .../protocols/actions/ImapAction.java | 19 + .../protocols/actions/ImapDeleteEmail.java | 37 + .../protocols/actions/ImapListEmails.java | 76 ++ .../protocols/actions/ImapMoveEmail.java | 49 + .../protocols/actions/ImapReadEmail.java | 37 + .../protocols/actions/ImapSearchEmails.java | 36 + .../protocols/actions/Pop3Action.java | 19 + .../protocols/actions/Pop3DeleteEmail.java | 25 + .../protocols/actions/Pop3ListEmails.java | 63 + .../protocols/actions/Pop3ReadEmail.java | 37 + .../protocols/actions/Pop3SearchEmails.java | 26 + .../protocols/actions/SearchCriteria.java | 13 + .../protocols/actions/SearchOperator.java | 12 + .../protocols/actions/SmtpAction.java | 18 + .../protocols/actions/SmtpSendEmail.java | 83 ++ .../protocols/actions/SortFieldImap.java | 13 + .../protocols/actions/SortFieldPop3.java | 12 + .../outbound/protocols/actions/SortOrder.java | 24 + .../email/response/DeleteEmailResponse.java | 9 + .../email/response/ListEmailsResponse.java | 12 + .../email/response/MoveEmailResponse.java | 9 + .../email/response/ReadEmailResponse.java | 17 + .../email/response/SearchEmailsResponse.java | 9 + .../email/response/SendEmailResponse.java | 9 + ...tor.api.inbound.InboundConnectorExecutable | 1 + ...tor.api.outbound.OutboundConnectorFunction | 1 + connectors/email/src/main/resources/icon.svg | 13 + .../client/jakarta/JakartaExecutorTest.java | 634 +++++++++ .../client/jakarta/JakartaUtilsTest.java | 184 +++ .../email/client/jakarta/TestFolder.java | 122 ++ .../email/client/jakarta/TestMessage.java | 285 ++++ .../email/client/jakarta/TestStore.java | 36 + .../resources/criterias/body-criteria.json | 22 + .../resources/criterias/simple-criteria.json | 22 + connectors/pom.xml | 1 + 74 files changed, 8761 insertions(+), 2 deletions(-) create mode 100644 connectors/email/element-templates/email-inbound-connector-boundary.json create mode 100644 connectors/email/element-templates/email-inbound-connector-intermediate.json create mode 100644 connectors/email/element-templates/email-message-start-event-connector.json create mode 100644 connectors/email/element-templates/email-outbound-connector.json create mode 100644 connectors/email/element-templates/hybrid/email-inbound-connector-boundary-hybrid.json create mode 100644 connectors/email/element-templates/hybrid/email-inbound-connector-intermediate-hybrid.json create mode 100644 connectors/email/element-templates/hybrid/email-outbound-connector-hybrid.json create mode 100644 connectors/email/element-templates/hybrid/hybrid-email-message-start-event-connector-hybrid.json create mode 100644 connectors/email/pom.xml create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/authentication/Authentication.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/authentication/SimpleAuthentication.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/EmailActionExecutor.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/EmailListener.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/Email.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailAttachment.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailBody.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailActionExecutor.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailListener.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaUtils.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/config/Configuration.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/config/CryptographicProtocol.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/config/ImapConfig.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/config/Pop3Config.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/config/SmtpConfig.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/inbound/EmailConnectorExecutable.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailInboundConnectorProperties.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailListenerConfig.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/inbound/model/HandlingStrategy.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/inbound/model/InitialPollingConfig.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/EmailConnectorFunction.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/model/EmailRequest.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Imap.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Pop3.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Protocol.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Smtp.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Action.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapAction.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapDeleteEmail.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapListEmails.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapMoveEmail.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapReadEmail.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapSearchEmails.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3Action.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3DeleteEmail.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ListEmails.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ReadEmail.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3SearchEmails.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchCriteria.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchOperator.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpAction.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpSendEmail.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldImap.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldPop3.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortOrder.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/response/DeleteEmailResponse.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/response/ListEmailsResponse.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/response/MoveEmailResponse.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/response/ReadEmailResponse.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/response/SearchEmailsResponse.java create mode 100644 connectors/email/src/main/java/io/camunda/connector/email/response/SendEmailResponse.java create mode 100644 connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.inbound.InboundConnectorExecutable create mode 100644 connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction create mode 100644 connectors/email/src/main/resources/icon.svg create mode 100644 connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaExecutorTest.java create mode 100644 connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaUtilsTest.java create mode 100644 connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestFolder.java create mode 100644 connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestMessage.java create mode 100644 connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestStore.java create mode 100644 connectors/email/src/test/resources/criterias/body-criteria.json create mode 100644 connectors/email/src/test/resources/criterias/simple-criteria.json diff --git a/bundle/default-bundle/pom.xml b/bundle/default-bundle/pom.xml index 571a45cbf3..85750d0890 100644 --- a/bundle/default-bundle/pom.xml +++ b/bundle/default-bundle/pom.xml @@ -105,6 +105,10 @@ io.camunda.connector connector-aws-sagemaker + + io.camunda.connector + connector-email + diff --git a/bundle/pom.xml b/bundle/pom.xml index 997170cf70..f2a49cba43 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -144,6 +144,11 @@ connector-aws-sagemaker ${project.version} + + io.camunda.connector + connector-email + ${project.version} + diff --git a/connector-sdk/core/src/main/java/io/camunda/connector/api/json/ConnectorsObjectMapperSupplier.java b/connector-sdk/core/src/main/java/io/camunda/connector/api/json/ConnectorsObjectMapperSupplier.java index 1a35dab5b5..d011401861 100644 --- a/connector-sdk/core/src/main/java/io/camunda/connector/api/json/ConnectorsObjectMapperSupplier.java +++ b/connector-sdk/core/src/main/java/io/camunda/connector/api/json/ConnectorsObjectMapperSupplier.java @@ -28,8 +28,6 @@ /** Default ObjectMapper supplier to be used by the connector runtime. */ public class ConnectorsObjectMapperSupplier { - private ConnectorsObjectMapperSupplier() {} - public static ObjectMapper DEFAULT_MAPPER = JsonMapper.builder() .addModules(new JacksonModuleFeelFunction(), new Jdk8Module(), new JavaTimeModule()) @@ -40,6 +38,8 @@ private ConnectorsObjectMapperSupplier() {} .disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) .build(); + private ConnectorsObjectMapperSupplier() {} + public static ObjectMapper getCopy() { return DEFAULT_MAPPER.copy(); } diff --git a/connectors/email/element-templates/email-inbound-connector-boundary.json b/connectors/email/element-templates/email-inbound-connector-boundary.json new file mode 100644 index 0000000000..5cb7699dc1 --- /dev/null +++ b/connectors/email/element-templates/email-inbound-connector-boundary.json @@ -0,0 +1,419 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Email Boundary Event Connector", + "id" : "io.camunda.connectors.inbound.EmailBoundary.v1", + "description" : "Consume emails", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:BoundaryEvent" ], + "elementType" : { + "value" : "bpmn:BoundaryEvent", + "eventDefinition" : "bpmn:MessageEventDefinition" + }, + "groups" : [ { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Imap Details" + }, { + "id" : "listenerInfos", + "label" : "Listener information" + }, { + "id" : "activation", + "label" : "Activation" + }, { + "id" : "correlation", + "label" : "Correlation", + "tooltip" : "Learn more about message correlation in the documentation." + }, { + "id" : "deduplication", + "label" : "Deduplication", + "tooltip" : "Deduplication allows you to configure multiple inbound connector elements to reuse the same backend (consumer/thread/endpoint) by sharing the same deduplication ID." + }, { + "id" : "output", + "label" : "Output mapping" + } ], + "properties" : [ { + "value" : "io.camunda:connector-email-inbound:1", + "binding" : { + "name" : "inbound.type", + "type" : "zeebe:property" + }, + "type" : "Hidden" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:property" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.folderToListen", + "label" : "Folder to listen", + "optional" : true, + "feel" : "optional", + "group" : "listenerInfos", + "binding" : { + "name" : "data.folderToListen", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the names of the folders you wish to monitor, separated by commas, for new emails or changes (e.g., 'INBOX, Sent, Archive'). If left blank, the listener will default to monitoring the 'INBOX' folder.", + "type" : "String" + }, { + "id" : "data.initialPollingConfig", + "label" : "Sync strategy", + "optional" : false, + "value" : "UNSEEN", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.initialPollingConfig", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired polling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Unseen emails will be sync", + "value" : "UNSEEN" + }, { + "name" : "No initial sync. Only new emails", + "value" : "NONE" + }, { + "name" : "All emails will be sync", + "value" : "ALL" + } ] + }, { + "id" : "data.handlingStrategy", + "label" : "Handling strategy", + "optional" : false, + "value" : "READ", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.handlingStrategy", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired handling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Mark as read after processing", + "value" : "READ" + }, { + "name" : "Do nothing", + "value" : "NO_HANDLING" + }, { + "name" : "Delete after processing", + "value" : "DELETE" + }, { + "name" : "Move to another folder after processing", + "value" : "MOVE" + } ] + }, { + "id" : "data.targetFolder", + "label" : "Choose the target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.targetFolder", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "data.handlingStrategy", + "equals" : "MOVE", + "type" : "simple" + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "activationCondition", + "label" : "Activation condition", + "description" : "Condition under which the Connector triggers. Leave empty to catch all events", + "optional" : true, + "feel" : "required", + "group" : "activation", + "binding" : { + "name" : "activationCondition", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "consumeUnmatchedEvents", + "label" : "Consume unmatched events", + "value" : true, + "group" : "activation", + "binding" : { + "name" : "consumeUnmatchedEvents", + "type" : "zeebe:property" + }, + "tooltip" : "Unmatched events are rejected by default, allowing the upstream service to handle the error. Check this box to consume unmatched events and return a success response", + "type" : "Boolean" + }, { + "id" : "correlationKeyProcess", + "label" : "Correlation key (process)", + "description" : "Sets up the correlation key from process variables", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKey", + "type" : "bpmn:Message#zeebe:subscription#property" + }, + "type" : "String" + }, { + "id" : "correlationKeyPayload", + "label" : "Correlation key (payload)", + "description" : "Extracts the correlation key from the incoming message payload", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKeyExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageIdExpression", + "label" : "Message ID expression", + "description" : "Expression to extract unique identifier of a message", + "optional" : true, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "messageIdExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageTtl", + "label" : "Message TTL", + "description" : "Time-to-live for the message in the broker (ISO-8601 duration)", + "optional" : true, + "constraints" : { + "notEmpty" : false, + "pattern" : { + "value" : "^(PT.*|)$", + "message" : "must be an ISO-8601 duration" + } + }, + "feel" : "optional", + "group" : "correlation", + "binding" : { + "name" : "messageTtl", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageNameUuid", + "generatedValue" : { + "type" : "uuid" + }, + "group" : "correlation", + "binding" : { + "name" : "name", + "type" : "bpmn:Message#property" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeManualFlag", + "label" : "Manual mode", + "description" : "By default, similar connectors receive the same deduplication ID. Customize by activating manual mode", + "value" : false, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationModeManualFlag", + "type" : "zeebe:property" + }, + "type" : "Boolean" + }, { + "id" : "deduplicationId", + "label" : "Deduplication ID", + "constraints" : { + "notEmpty" : true, + "pattern" : { + "value" : "^[a-zA-Z0-9_-]+$", + "message" : "can only contain alphanumeric characters, dashes, and underscores" + } + }, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationId", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationModeManualFlag", + "equals" : true, + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "deduplicationModeManual", + "value" : "MANUAL", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : true, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeAuto", + "value" : "AUTO", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : false, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "name" : "resultVariable", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "name" : "resultExpression", + "type" : "zeebe:property" + }, + "type" : "Text" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/element-templates/email-inbound-connector-intermediate.json b/connectors/email/element-templates/email-inbound-connector-intermediate.json new file mode 100644 index 0000000000..d5c332570f --- /dev/null +++ b/connectors/email/element-templates/email-inbound-connector-intermediate.json @@ -0,0 +1,419 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Email Intermediate Catch Event Connector", + "id" : "io.camunda.connectors.inbound.EmailIntermediate.v1", + "description" : "Consume emails", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:IntermediateCatchEvent", "bpmn:IntermediateThrowEvent" ], + "elementType" : { + "value" : "bpmn:IntermediateCatchEvent", + "eventDefinition" : "bpmn:MessageEventDefinition" + }, + "groups" : [ { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Imap Details" + }, { + "id" : "listenerInfos", + "label" : "Listener information" + }, { + "id" : "activation", + "label" : "Activation" + }, { + "id" : "correlation", + "label" : "Correlation", + "tooltip" : "Learn more about message correlation in the documentation." + }, { + "id" : "deduplication", + "label" : "Deduplication", + "tooltip" : "Deduplication allows you to configure multiple inbound connector elements to reuse the same backend (consumer/thread/endpoint) by sharing the same deduplication ID." + }, { + "id" : "output", + "label" : "Output mapping" + } ], + "properties" : [ { + "value" : "io.camunda:connector-email-inbound:1", + "binding" : { + "name" : "inbound.type", + "type" : "zeebe:property" + }, + "type" : "Hidden" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:property" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.folderToListen", + "label" : "Folder to listen", + "optional" : true, + "feel" : "optional", + "group" : "listenerInfos", + "binding" : { + "name" : "data.folderToListen", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the names of the folders you wish to monitor, separated by commas, for new emails or changes (e.g., 'INBOX, Sent, Archive'). If left blank, the listener will default to monitoring the 'INBOX' folder.", + "type" : "String" + }, { + "id" : "data.initialPollingConfig", + "label" : "Sync strategy", + "optional" : false, + "value" : "UNSEEN", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.initialPollingConfig", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired polling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Unseen emails will be sync", + "value" : "UNSEEN" + }, { + "name" : "No initial sync. Only new emails", + "value" : "NONE" + }, { + "name" : "All emails will be sync", + "value" : "ALL" + } ] + }, { + "id" : "data.handlingStrategy", + "label" : "Handling strategy", + "optional" : false, + "value" : "READ", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.handlingStrategy", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired handling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Mark as read after processing", + "value" : "READ" + }, { + "name" : "Do nothing", + "value" : "NO_HANDLING" + }, { + "name" : "Delete after processing", + "value" : "DELETE" + }, { + "name" : "Move to another folder after processing", + "value" : "MOVE" + } ] + }, { + "id" : "data.targetFolder", + "label" : "Choose the target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.targetFolder", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "data.handlingStrategy", + "equals" : "MOVE", + "type" : "simple" + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "activationCondition", + "label" : "Activation condition", + "description" : "Condition under which the Connector triggers. Leave empty to catch all events", + "optional" : true, + "feel" : "required", + "group" : "activation", + "binding" : { + "name" : "activationCondition", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "consumeUnmatchedEvents", + "label" : "Consume unmatched events", + "value" : true, + "group" : "activation", + "binding" : { + "name" : "consumeUnmatchedEvents", + "type" : "zeebe:property" + }, + "tooltip" : "Unmatched events are rejected by default, allowing the upstream service to handle the error. Check this box to consume unmatched events and return a success response", + "type" : "Boolean" + }, { + "id" : "correlationKeyProcess", + "label" : "Correlation key (process)", + "description" : "Sets up the correlation key from process variables", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKey", + "type" : "bpmn:Message#zeebe:subscription#property" + }, + "type" : "String" + }, { + "id" : "correlationKeyPayload", + "label" : "Correlation key (payload)", + "description" : "Extracts the correlation key from the incoming message payload", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKeyExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageIdExpression", + "label" : "Message ID expression", + "description" : "Expression to extract unique identifier of a message", + "optional" : true, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "messageIdExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageTtl", + "label" : "Message TTL", + "description" : "Time-to-live for the message in the broker (ISO-8601 duration)", + "optional" : true, + "constraints" : { + "notEmpty" : false, + "pattern" : { + "value" : "^(PT.*|)$", + "message" : "must be an ISO-8601 duration" + } + }, + "feel" : "optional", + "group" : "correlation", + "binding" : { + "name" : "messageTtl", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageNameUuid", + "generatedValue" : { + "type" : "uuid" + }, + "group" : "correlation", + "binding" : { + "name" : "name", + "type" : "bpmn:Message#property" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeManualFlag", + "label" : "Manual mode", + "description" : "By default, similar connectors receive the same deduplication ID. Customize by activating manual mode", + "value" : false, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationModeManualFlag", + "type" : "zeebe:property" + }, + "type" : "Boolean" + }, { + "id" : "deduplicationId", + "label" : "Deduplication ID", + "constraints" : { + "notEmpty" : true, + "pattern" : { + "value" : "^[a-zA-Z0-9_-]+$", + "message" : "can only contain alphanumeric characters, dashes, and underscores" + } + }, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationId", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationModeManualFlag", + "equals" : true, + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "deduplicationModeManual", + "value" : "MANUAL", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : true, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeAuto", + "value" : "AUTO", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : false, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "name" : "resultVariable", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "name" : "resultExpression", + "type" : "zeebe:property" + }, + "type" : "Text" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/element-templates/email-message-start-event-connector.json b/connectors/email/element-templates/email-message-start-event-connector.json new file mode 100644 index 0000000000..dad5a29d38 --- /dev/null +++ b/connectors/email/element-templates/email-message-start-event-connector.json @@ -0,0 +1,447 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Email Message Start Event Connector", + "id" : "io.camunda.connectors.inbound.EmailMessageStart.v1", + "description" : "Consume emails", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:StartEvent" ], + "elementType" : { + "value" : "bpmn:StartEvent", + "eventDefinition" : "bpmn:MessageEventDefinition" + }, + "groups" : [ { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Imap Details" + }, { + "id" : "listenerInfos", + "label" : "Listener information" + }, { + "id" : "activation", + "label" : "Activation" + }, { + "id" : "correlation", + "label" : "Correlation", + "tooltip" : "Learn more about message correlation in the documentation." + }, { + "id" : "deduplication", + "label" : "Deduplication", + "tooltip" : "Deduplication allows you to configure multiple inbound connector elements to reuse the same backend (consumer/thread/endpoint) by sharing the same deduplication ID." + }, { + "id" : "output", + "label" : "Output mapping" + } ], + "properties" : [ { + "value" : "io.camunda:connector-email-inbound:1", + "binding" : { + "name" : "inbound.type", + "type" : "zeebe:property" + }, + "type" : "Hidden" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:property" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.folderToListen", + "label" : "Folder to listen", + "optional" : true, + "feel" : "optional", + "group" : "listenerInfos", + "binding" : { + "name" : "data.folderToListen", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the names of the folders you wish to monitor, separated by commas, for new emails or changes (e.g., 'INBOX, Sent, Archive'). If left blank, the listener will default to monitoring the 'INBOX' folder.", + "type" : "String" + }, { + "id" : "data.initialPollingConfig", + "label" : "Sync strategy", + "optional" : false, + "value" : "UNSEEN", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.initialPollingConfig", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired polling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Unseen emails will be sync", + "value" : "UNSEEN" + }, { + "name" : "No initial sync. Only new emails", + "value" : "NONE" + }, { + "name" : "All emails will be sync", + "value" : "ALL" + } ] + }, { + "id" : "data.handlingStrategy", + "label" : "Handling strategy", + "optional" : false, + "value" : "READ", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.handlingStrategy", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired handling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Mark as read after processing", + "value" : "READ" + }, { + "name" : "Do nothing", + "value" : "NO_HANDLING" + }, { + "name" : "Delete after processing", + "value" : "DELETE" + }, { + "name" : "Move to another folder after processing", + "value" : "MOVE" + } ] + }, { + "id" : "data.targetFolder", + "label" : "Choose the target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.targetFolder", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "data.handlingStrategy", + "equals" : "MOVE", + "type" : "simple" + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "activationCondition", + "label" : "Activation condition", + "description" : "Condition under which the Connector triggers. Leave empty to catch all events", + "optional" : true, + "feel" : "required", + "group" : "activation", + "binding" : { + "name" : "activationCondition", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "consumeUnmatchedEvents", + "label" : "Consume unmatched events", + "value" : true, + "group" : "activation", + "binding" : { + "name" : "consumeUnmatchedEvents", + "type" : "zeebe:property" + }, + "tooltip" : "Unmatched events are rejected by default, allowing the upstream service to handle the error. Check this box to consume unmatched events and return a success response", + "type" : "Boolean" + }, { + "id" : "correlationRequired", + "label" : "Subprocess correlation required", + "description" : "Indicates whether correlation is required. This is needed for event-based subprocess message start events", + "value" : "notRequired", + "group" : "correlation", + "binding" : { + "name" : "correlationRequired", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Correlation not required", + "value" : "notRequired" + }, { + "name" : "Correlation required", + "value" : "required" + } ] + }, { + "id" : "correlationKeyProcess", + "label" : "Correlation key (process)", + "description" : "Sets up the correlation key from process variables", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKey", + "type" : "bpmn:Message#zeebe:subscription#property" + }, + "condition" : { + "property" : "correlationRequired", + "equals" : "required", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "correlationKeyPayload", + "label" : "Correlation key (payload)", + "description" : "Extracts the correlation key from the incoming message payload", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKeyExpression", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "correlationRequired", + "equals" : "required", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "messageTtl", + "label" : "Message TTL", + "description" : "Time-to-live for the message in the broker (ISO-8601 duration)", + "optional" : true, + "constraints" : { + "notEmpty" : false, + "pattern" : { + "value" : "^(PT.*|)$", + "message" : "must be an ISO-8601 duration" + } + }, + "feel" : "optional", + "group" : "correlation", + "binding" : { + "name" : "messageTtl", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageIdExpression", + "label" : "Message ID expression", + "description" : "Expression to extract unique identifier of a message", + "optional" : true, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "messageIdExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageNameUuid", + "generatedValue" : { + "type" : "uuid" + }, + "group" : "correlation", + "binding" : { + "name" : "name", + "type" : "bpmn:Message#property" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeManualFlag", + "label" : "Manual mode", + "description" : "By default, similar connectors receive the same deduplication ID. Customize by activating manual mode", + "value" : false, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationModeManualFlag", + "type" : "zeebe:property" + }, + "type" : "Boolean" + }, { + "id" : "deduplicationId", + "label" : "Deduplication ID", + "constraints" : { + "notEmpty" : true, + "pattern" : { + "value" : "^[a-zA-Z0-9_-]+$", + "message" : "can only contain alphanumeric characters, dashes, and underscores" + } + }, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationId", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationModeManualFlag", + "equals" : true, + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "deduplicationModeManual", + "value" : "MANUAL", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : true, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeAuto", + "value" : "AUTO", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : false, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "name" : "resultVariable", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "name" : "resultExpression", + "type" : "zeebe:property" + }, + "type" : "Text" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/element-templates/email-outbound-connector.json b/connectors/email/element-templates/email-outbound-connector.json new file mode 100644 index 0000000000..a23f2faf47 --- /dev/null +++ b/connectors/email/element-templates/email-outbound-connector.json @@ -0,0 +1,1184 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Email Connector", + "id" : "io.camunda.connectors.email.v1", + "description" : "Execute email requests", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:Task" ], + "elementType" : { + "value" : "bpmn:ServiceTask" + }, + "groups" : [ { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Protocol" + }, { + "id" : "smtpAction", + "label" : "SMTP Action" + }, { + "id" : "pop3Action", + "label" : "POP3 Action" + }, { + "id" : "imapAction", + "label" : "IMAP Action" + }, { + "id" : "sendEmailSmtp", + "label" : "Send Email" + }, { + "id" : "listEmailsPop3", + "label" : "List Emails" + }, { + "id" : "searchEmailsPop3", + "label" : "Search Emails" + }, { + "id" : "deleteEmailPop3", + "label" : "Delete Email" + }, { + "id" : "readEmailPop3", + "label" : "Read Email" + }, { + "id" : "listEmailsImap", + "label" : "List Email" + }, { + "id" : "searchEmailsImap", + "label" : "Search Emails" + }, { + "id" : "readEmailImap", + "label" : "Read Email" + }, { + "id" : "deleteEmailImap", + "label" : "Read Email" + }, { + "id" : "moveEmailImap", + "label" : "Move Emails" + }, { + "id" : "output", + "label" : "Output mapping" + }, { + "id" : "error", + "label" : "Error handling" + }, { + "id" : "retries", + "label" : "Retries" + } ], + "properties" : [ { + "value" : "io.camunda:email:1", + "binding" : { + "property" : "type", + "type" : "zeebe:taskDefinition" + }, + "type" : "Hidden" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "protocol", + "label" : "Email Protocol", + "value" : "smtp", + "group" : "protocol", + "binding" : { + "name" : "protocol", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "IMAP", + "value" : "imap" + }, { + "name" : "POP3", + "value" : "pop3" + }, { + "name" : "SMTP", + "value" : "smtp" + } ] + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.pop3Host", + "label" : "POP3 Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.pop3Config.pop3Host", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "tooltip" : "Enter the address of the POP3 server if you want to download your emails to a single device. This server is typically used for retrieving emails without syncing. (e.g., pop.example.com)", + "type" : "String" + }, { + "id" : "data.pop3Port", + "label" : "POP3 Port", + "optional" : false, + "value" : "995", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.pop3Config.pop3Port", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "tooltip" : "Enter the port number for connecting to the POP3 server. The standard port is 995 for secure connections with SSL/TLS, or 110 for non-secure connections.", + "type" : "String" + }, { + "id" : "pop3CryptographicProtocol", + "label" : "Cryptographic protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.pop3Config.pop3CryptographicProtocol", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.smtpHost", + "label" : "SMTP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.smtpConfig.smtpHost", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "tooltip" : "Provide the address of the SMTP server used for sending emails. This server handles the delivery of your outgoing messages. (e.g., smtp.example.com)", + "type" : "String" + }, { + "id" : "data.smtpPort", + "label" : "SMTP Port", + "optional" : false, + "value" : "587", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.smtpConfig.smtpPort", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "tooltip" : "Enter the port number for connecting to the SMTP server. Typically, port 587 is used for secure connections with STARTTLS, port 465 for secure connections using SSL/TLS, and port 25 for non-secure connections.", + "type" : "String" + }, { + "id" : "smtpCryptographicProtocol", + "label" : "Cryptographic protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.smtpConfig.smtpCryptographicProtocol", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.smtpActionDiscriminator", + "label" : "SMTP action", + "value" : "sendEmailSmtp", + "group" : "smtpAction", + "binding" : { + "name" : "data.smtpActionDiscriminator", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Send Email", + "value" : "sendEmailSmtp" + } ] + }, { + "id" : "data.pop3ActionDiscriminator", + "label" : "POP3 action", + "value" : "listEmailsPop3", + "group" : "pop3Action", + "binding" : { + "name" : "data.pop3ActionDiscriminator", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Delete Email", + "value" : "deleteEmailPop3" + }, { + "name" : "List Emails", + "value" : "listEmailsPop3" + }, { + "name" : "Read Email", + "value" : "readEmailPop3" + }, { + "name" : "Search emails", + "value" : "searchEmailsPop3" + } ] + }, { + "id" : "data.imapActionDiscriminator", + "label" : "IMAP action", + "value" : "listEmailsImap", + "group" : "imapAction", + "binding" : { + "name" : "data.imapActionDiscriminator", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Delete an email", + "value" : "deleteEmailImap" + }, { + "name" : "List emails", + "value" : "listEmailsImap" + }, { + "name" : "Move email", + "value" : "moveEmailImap" + }, { + "name" : "Read an email", + "value" : "readEmailImap" + }, { + "name" : "Search emails", + "value" : "searchEmailsImap" + } ] + }, { + "id" : "smtpFrom", + "label" : "From", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.from", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpTo", + "label" : "To", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.to", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpCc", + "label" : "Cc", + "optional" : true, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.cc", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpBcc", + "label" : "Bcc", + "optional" : true, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.bcc", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpSubject", + "label" : "Subject", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.subject", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Email's subject", + "type" : "String" + }, { + "id" : "smtpBody", + "label" : "Email Content", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.body", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Email's content", + "type" : "Text" + }, { + "id" : "pop3maxToBeRead", + "label" : "Maximum number of emails to be read", + "optional" : false, + "value" : "100", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsPop3", + "binding" : { + "name" : "data.pop3Action.maxToBeRead", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "listEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the maximum number of emails to be read from the specified folder. This limits the number of emails fetched to avoid performance issues with large mailboxes. The default value is set to 100.", + "type" : "String" + }, { + "id" : "pop3SortField", + "label" : "Sort emails by", + "optional" : false, + "value" : "SENT_DATE", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsPop3", + "binding" : { + "name" : "data.pop3Action.sortField", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "listEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Choose the criterion by which the listed emails should be sorted. The default sorting is by 'Sent Date'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "Sent Date", + "value" : "SENT_DATE" + }, { + "name" : "Size", + "value" : "SIZE" + } ] + }, { + "id" : "pop3SortOrder", + "label" : "Sort order", + "optional" : false, + "value" : "ASC", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsPop3", + "binding" : { + "name" : "data.pop3Action.sortOrder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "listEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Select the sort order for the emails. Choose 'ASC' for ascending order or 'DESC' for descending order. Ascending order will list older emails first, while descending order will list newer emails first. The default sort order is 'ASC'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "ASC", + "value" : "ASC" + }, { + "name" : "DESC", + "value" : "DESC" + } ] + }, { + "id" : "searchStringEmailPop3", + "label" : "Search criteria", + "optional" : true, + "feel" : "required", + "group" : "searchEmailsPop3", + "binding" : { + "name" : "data.pop3Action.criteria", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "searchEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Define the search criteria using supported keywords and syntax to filter emails. Refer to our detailed documentation for full search syntax and examples: [Email Documentation](https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/).", + "type" : "Text" + }, { + "id" : "pop3MessageIdDelete", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "deleteEmailPop3", + "binding" : { + "name" : "data.pop3Action.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "deleteEmailPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "pop3MessageIdRead", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "readEmailPop3", + "binding" : { + "name" : "data.pop3Action.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "readEmailPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "deleteOnRead", + "label" : "Delete after reading", + "optional" : false, + "value" : false, + "feel" : "optional", + "group" : "readEmailPop3", + "binding" : { + "name" : "data.pop3Action.deleteOnRead", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "readEmailPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Enable this option if you want the email to be automatically deleted from the server after it is read. By default, this option is turned off to retain emails on the server.", + "type" : "Boolean" + }, { + "id" : "imapMaxToBeRead", + "label" : "Maximum number of emails to be read", + "optional" : false, + "value" : "100", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.maxToBeRead", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the maximum number of emails to be read from the specified folder. This limits the number of emails fetched to avoid performance issues with large mailboxes. The default value is set to 100.", + "type" : "String" + }, { + "id" : "imapListEmailsFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.listEmailsFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the folder from which you want to list emails (e.g., 'INBOX', 'Sent', 'Drafts'). If left blank, emails will be listed from the default 'INBOX' folder.", + "type" : "String" + }, { + "id" : "imapSortField", + "label" : "Sort emails by", + "optional" : false, + "value" : "RECEIVED_DATE", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.sortField", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Choose the criterion by which the listed emails should be sorted. The default sorting is by 'Received Date'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "Received Date", + "value" : "RECEIVED_DATE" + }, { + "name" : "Sent Date", + "value" : "SENT_DATE" + }, { + "name" : "Size", + "value" : "SIZE" + } ] + }, { + "id" : "imapSortOrder", + "label" : "Sort order", + "optional" : false, + "value" : "ASC", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.sortOrder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Select the sort order for the emails. Choose 'ASC' for ascending order or 'DESC' for descending order. Ascending order will list older emails first, while descending order will list newer emails first. The default sort order is 'ASC'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "ASC", + "value" : "ASC" + }, { + "name" : "DESC", + "value" : "DESC" + } ] + }, { + "id" : "searchStringEmailImap", + "label" : "Search criteria", + "optional" : true, + "feel" : "required", + "group" : "searchEmailsImap", + "binding" : { + "name" : "data.imapAction.criteria", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "searchEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Define the search criteria using supported keywords and syntax to filter emails. Refer to our detailed documentation for full search syntax and examples: [Email Documentation](https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/).", + "type" : "Text" + }, { + "id" : "searchEmailFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "searchEmailsImap", + "binding" : { + "name" : "data.imapAction.searchEmailFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "searchEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the folder in which to conduct the email search. If left blank, the search will default to the 'INBOX' folder. You may also specify subfolders using a dot-separated path (e.g., 'INBOX.Archives').", + "type" : "String" + }, { + "id" : "imapMessageIdRead", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "readEmailImap", + "binding" : { + "name" : "data.imapAction.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "readEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "readEmailFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "readEmailImap", + "binding" : { + "name" : "data.imapAction.readEmailFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "readEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the name of the folder from which you wish to read emails. If left blank, emails will be read from the default 'INBOX' folder.", + "type" : "String" + }, { + "id" : "imapMessageIdDelete", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "deleteEmailImap", + "binding" : { + "name" : "data.imapAction.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "deleteEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "deleteEmailFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "deleteEmailImap", + "binding" : { + "name" : "data.imapAction.deleteEmailFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "deleteEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the name of the folder from which you want to delete emails. If left blank, the default 'INBOX' will be used. For example, you can enter 'Trash' to delete emails from the Trash folder.", + "type" : "String" + }, { + "id" : "imapMessageIdMove", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "moveEmailImap", + "binding" : { + "name" : "data.imapAction.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "moveEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "data.fromFolder", + "label" : "Source folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "moveEmailImap", + "binding" : { + "name" : "data.imapAction.fromFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "moveEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the name of the folder from which the emails will be moved. This field is required. For example, enter 'INBOX' to move emails from your Inbox.", + "type" : "String" + }, { + "id" : "data.toFolder", + "label" : "Target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "moveEmailImap", + "binding" : { + "name" : "data.imapAction.toFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "moveEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "key" : "resultVariable", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "key" : "resultExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "errorExpression", + "label" : "Error expression", + "description" : "Expression to handle errors. Details in the documentation.", + "feel" : "required", + "group" : "error", + "binding" : { + "key" : "errorExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "retryCount", + "label" : "Retries", + "description" : "Number of retries", + "value" : "3", + "feel" : "optional", + "group" : "retries", + "binding" : { + "property" : "retries", + "type" : "zeebe:taskDefinition" + }, + "type" : "String" + }, { + "id" : "retryBackoff", + "label" : "Retry backoff", + "description" : "ISO-8601 duration to wait between retries", + "value" : "PT0S", + "feel" : "optional", + "group" : "retries", + "binding" : { + "key" : "retryBackoff", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/element-templates/hybrid/email-inbound-connector-boundary-hybrid.json b/connectors/email/element-templates/hybrid/email-inbound-connector-boundary-hybrid.json new file mode 100644 index 0000000000..953e66573d --- /dev/null +++ b/connectors/email/element-templates/hybrid/email-inbound-connector-boundary-hybrid.json @@ -0,0 +1,424 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Hybrid Email Boundary Event Connector", + "id" : "io.camunda.connectors.inbound.EmailBoundary.v1-hybrid", + "description" : "Consume emails", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:BoundaryEvent" ], + "elementType" : { + "value" : "bpmn:BoundaryEvent", + "eventDefinition" : "bpmn:MessageEventDefinition" + }, + "groups" : [ { + "id" : "connectorType", + "label" : "Connector type" + }, { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Imap Details" + }, { + "id" : "listenerInfos", + "label" : "Listener information" + }, { + "id" : "activation", + "label" : "Activation" + }, { + "id" : "correlation", + "label" : "Correlation", + "tooltip" : "Learn more about message correlation in the documentation." + }, { + "id" : "deduplication", + "label" : "Deduplication", + "tooltip" : "Deduplication allows you to configure multiple inbound connector elements to reuse the same backend (consumer/thread/endpoint) by sharing the same deduplication ID." + }, { + "id" : "output", + "label" : "Output mapping" + } ], + "properties" : [ { + "id" : "connectorType", + "value" : "io.camunda:connector-email-inbound:1", + "group" : "connectorType", + "binding" : { + "name" : "inbound.type", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:property" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.folderToListen", + "label" : "Folder to listen", + "optional" : true, + "feel" : "optional", + "group" : "listenerInfos", + "binding" : { + "name" : "data.folderToListen", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the names of the folders you wish to monitor, separated by commas, for new emails or changes (e.g., 'INBOX, Sent, Archive'). If left blank, the listener will default to monitoring the 'INBOX' folder.", + "type" : "String" + }, { + "id" : "data.initialPollingConfig", + "label" : "Sync strategy", + "optional" : false, + "value" : "UNSEEN", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.initialPollingConfig", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired polling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Unseen emails will be sync", + "value" : "UNSEEN" + }, { + "name" : "No initial sync. Only new emails", + "value" : "NONE" + }, { + "name" : "All emails will be sync", + "value" : "ALL" + } ] + }, { + "id" : "data.handlingStrategy", + "label" : "Handling strategy", + "optional" : false, + "value" : "READ", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.handlingStrategy", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired handling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Mark as read after processing", + "value" : "READ" + }, { + "name" : "Do nothing", + "value" : "NO_HANDLING" + }, { + "name" : "Delete after processing", + "value" : "DELETE" + }, { + "name" : "Move to another folder after processing", + "value" : "MOVE" + } ] + }, { + "id" : "data.targetFolder", + "label" : "Choose the target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.targetFolder", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "data.handlingStrategy", + "equals" : "MOVE", + "type" : "simple" + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "activationCondition", + "label" : "Activation condition", + "description" : "Condition under which the Connector triggers. Leave empty to catch all events", + "optional" : true, + "feel" : "required", + "group" : "activation", + "binding" : { + "name" : "activationCondition", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "consumeUnmatchedEvents", + "label" : "Consume unmatched events", + "value" : true, + "group" : "activation", + "binding" : { + "name" : "consumeUnmatchedEvents", + "type" : "zeebe:property" + }, + "tooltip" : "Unmatched events are rejected by default, allowing the upstream service to handle the error. Check this box to consume unmatched events and return a success response", + "type" : "Boolean" + }, { + "id" : "correlationKeyProcess", + "label" : "Correlation key (process)", + "description" : "Sets up the correlation key from process variables", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKey", + "type" : "bpmn:Message#zeebe:subscription#property" + }, + "type" : "String" + }, { + "id" : "correlationKeyPayload", + "label" : "Correlation key (payload)", + "description" : "Extracts the correlation key from the incoming message payload", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKeyExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageIdExpression", + "label" : "Message ID expression", + "description" : "Expression to extract unique identifier of a message", + "optional" : true, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "messageIdExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageTtl", + "label" : "Message TTL", + "description" : "Time-to-live for the message in the broker (ISO-8601 duration)", + "optional" : true, + "constraints" : { + "notEmpty" : false, + "pattern" : { + "value" : "^(PT.*|)$", + "message" : "must be an ISO-8601 duration" + } + }, + "feel" : "optional", + "group" : "correlation", + "binding" : { + "name" : "messageTtl", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageNameUuid", + "generatedValue" : { + "type" : "uuid" + }, + "group" : "correlation", + "binding" : { + "name" : "name", + "type" : "bpmn:Message#property" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeManualFlag", + "label" : "Manual mode", + "description" : "By default, similar connectors receive the same deduplication ID. Customize by activating manual mode", + "value" : false, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationModeManualFlag", + "type" : "zeebe:property" + }, + "type" : "Boolean" + }, { + "id" : "deduplicationId", + "label" : "Deduplication ID", + "constraints" : { + "notEmpty" : true, + "pattern" : { + "value" : "^[a-zA-Z0-9_-]+$", + "message" : "can only contain alphanumeric characters, dashes, and underscores" + } + }, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationId", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationModeManualFlag", + "equals" : true, + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "deduplicationModeManual", + "value" : "MANUAL", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : true, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeAuto", + "value" : "AUTO", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : false, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "name" : "resultVariable", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "name" : "resultExpression", + "type" : "zeebe:property" + }, + "type" : "Text" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/element-templates/hybrid/email-inbound-connector-intermediate-hybrid.json b/connectors/email/element-templates/hybrid/email-inbound-connector-intermediate-hybrid.json new file mode 100644 index 0000000000..dfa5f77f48 --- /dev/null +++ b/connectors/email/element-templates/hybrid/email-inbound-connector-intermediate-hybrid.json @@ -0,0 +1,424 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Hybrid Email Intermediate Catch Event Connector", + "id" : "io.camunda.connectors.inbound.EmailIntermediate.v1-hybrid", + "description" : "Consume emails", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:IntermediateCatchEvent", "bpmn:IntermediateThrowEvent" ], + "elementType" : { + "value" : "bpmn:IntermediateCatchEvent", + "eventDefinition" : "bpmn:MessageEventDefinition" + }, + "groups" : [ { + "id" : "connectorType", + "label" : "Connector type" + }, { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Imap Details" + }, { + "id" : "listenerInfos", + "label" : "Listener information" + }, { + "id" : "activation", + "label" : "Activation" + }, { + "id" : "correlation", + "label" : "Correlation", + "tooltip" : "Learn more about message correlation in the documentation." + }, { + "id" : "deduplication", + "label" : "Deduplication", + "tooltip" : "Deduplication allows you to configure multiple inbound connector elements to reuse the same backend (consumer/thread/endpoint) by sharing the same deduplication ID." + }, { + "id" : "output", + "label" : "Output mapping" + } ], + "properties" : [ { + "id" : "connectorType", + "value" : "io.camunda:connector-email-inbound:1", + "group" : "connectorType", + "binding" : { + "name" : "inbound.type", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:property" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.folderToListen", + "label" : "Folder to listen", + "optional" : true, + "feel" : "optional", + "group" : "listenerInfos", + "binding" : { + "name" : "data.folderToListen", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the names of the folders you wish to monitor, separated by commas, for new emails or changes (e.g., 'INBOX, Sent, Archive'). If left blank, the listener will default to monitoring the 'INBOX' folder.", + "type" : "String" + }, { + "id" : "data.initialPollingConfig", + "label" : "Sync strategy", + "optional" : false, + "value" : "UNSEEN", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.initialPollingConfig", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired polling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Unseen emails will be sync", + "value" : "UNSEEN" + }, { + "name" : "No initial sync. Only new emails", + "value" : "NONE" + }, { + "name" : "All emails will be sync", + "value" : "ALL" + } ] + }, { + "id" : "data.handlingStrategy", + "label" : "Handling strategy", + "optional" : false, + "value" : "READ", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.handlingStrategy", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired handling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Mark as read after processing", + "value" : "READ" + }, { + "name" : "Do nothing", + "value" : "NO_HANDLING" + }, { + "name" : "Delete after processing", + "value" : "DELETE" + }, { + "name" : "Move to another folder after processing", + "value" : "MOVE" + } ] + }, { + "id" : "data.targetFolder", + "label" : "Choose the target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.targetFolder", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "data.handlingStrategy", + "equals" : "MOVE", + "type" : "simple" + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "activationCondition", + "label" : "Activation condition", + "description" : "Condition under which the Connector triggers. Leave empty to catch all events", + "optional" : true, + "feel" : "required", + "group" : "activation", + "binding" : { + "name" : "activationCondition", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "consumeUnmatchedEvents", + "label" : "Consume unmatched events", + "value" : true, + "group" : "activation", + "binding" : { + "name" : "consumeUnmatchedEvents", + "type" : "zeebe:property" + }, + "tooltip" : "Unmatched events are rejected by default, allowing the upstream service to handle the error. Check this box to consume unmatched events and return a success response", + "type" : "Boolean" + }, { + "id" : "correlationKeyProcess", + "label" : "Correlation key (process)", + "description" : "Sets up the correlation key from process variables", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKey", + "type" : "bpmn:Message#zeebe:subscription#property" + }, + "type" : "String" + }, { + "id" : "correlationKeyPayload", + "label" : "Correlation key (payload)", + "description" : "Extracts the correlation key from the incoming message payload", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKeyExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageIdExpression", + "label" : "Message ID expression", + "description" : "Expression to extract unique identifier of a message", + "optional" : true, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "messageIdExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageTtl", + "label" : "Message TTL", + "description" : "Time-to-live for the message in the broker (ISO-8601 duration)", + "optional" : true, + "constraints" : { + "notEmpty" : false, + "pattern" : { + "value" : "^(PT.*|)$", + "message" : "must be an ISO-8601 duration" + } + }, + "feel" : "optional", + "group" : "correlation", + "binding" : { + "name" : "messageTtl", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageNameUuid", + "generatedValue" : { + "type" : "uuid" + }, + "group" : "correlation", + "binding" : { + "name" : "name", + "type" : "bpmn:Message#property" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeManualFlag", + "label" : "Manual mode", + "description" : "By default, similar connectors receive the same deduplication ID. Customize by activating manual mode", + "value" : false, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationModeManualFlag", + "type" : "zeebe:property" + }, + "type" : "Boolean" + }, { + "id" : "deduplicationId", + "label" : "Deduplication ID", + "constraints" : { + "notEmpty" : true, + "pattern" : { + "value" : "^[a-zA-Z0-9_-]+$", + "message" : "can only contain alphanumeric characters, dashes, and underscores" + } + }, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationId", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationModeManualFlag", + "equals" : true, + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "deduplicationModeManual", + "value" : "MANUAL", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : true, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeAuto", + "value" : "AUTO", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : false, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "name" : "resultVariable", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "name" : "resultExpression", + "type" : "zeebe:property" + }, + "type" : "Text" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/element-templates/hybrid/email-outbound-connector-hybrid.json b/connectors/email/element-templates/hybrid/email-outbound-connector-hybrid.json new file mode 100644 index 0000000000..1b5f19d9ae --- /dev/null +++ b/connectors/email/element-templates/hybrid/email-outbound-connector-hybrid.json @@ -0,0 +1,1189 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Hybrid Email Connector", + "id" : "io.camunda.connectors.email.v1-hybrid", + "description" : "Execute email requests", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:Task" ], + "elementType" : { + "value" : "bpmn:ServiceTask" + }, + "groups" : [ { + "id" : "taskDefinitionType", + "label" : "Task definition type" + }, { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Protocol" + }, { + "id" : "smtpAction", + "label" : "SMTP Action" + }, { + "id" : "pop3Action", + "label" : "POP3 Action" + }, { + "id" : "imapAction", + "label" : "IMAP Action" + }, { + "id" : "sendEmailSmtp", + "label" : "Send Email" + }, { + "id" : "listEmailsPop3", + "label" : "List Emails" + }, { + "id" : "searchEmailsPop3", + "label" : "Search Emails" + }, { + "id" : "deleteEmailPop3", + "label" : "Delete Email" + }, { + "id" : "readEmailPop3", + "label" : "Read Email" + }, { + "id" : "listEmailsImap", + "label" : "List Email" + }, { + "id" : "searchEmailsImap", + "label" : "Search Emails" + }, { + "id" : "readEmailImap", + "label" : "Read Email" + }, { + "id" : "deleteEmailImap", + "label" : "Read Email" + }, { + "id" : "moveEmailImap", + "label" : "Move Emails" + }, { + "id" : "output", + "label" : "Output mapping" + }, { + "id" : "error", + "label" : "Error handling" + }, { + "id" : "retries", + "label" : "Retries" + } ], + "properties" : [ { + "id" : "taskDefinitionType", + "value" : "io.camunda:email:1", + "group" : "taskDefinitionType", + "binding" : { + "property" : "type", + "type" : "zeebe:taskDefinition" + }, + "type" : "String" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "protocol", + "label" : "Email Protocol", + "value" : "smtp", + "group" : "protocol", + "binding" : { + "name" : "protocol", + "type" : "zeebe:input" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "IMAP", + "value" : "imap" + }, { + "name" : "POP3", + "value" : "pop3" + }, { + "name" : "SMTP", + "value" : "smtp" + } ] + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.pop3Host", + "label" : "POP3 Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.pop3Config.pop3Host", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "tooltip" : "Enter the address of the POP3 server if you want to download your emails to a single device. This server is typically used for retrieving emails without syncing. (e.g., pop.example.com)", + "type" : "String" + }, { + "id" : "data.pop3Port", + "label" : "POP3 Port", + "optional" : false, + "value" : "995", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.pop3Config.pop3Port", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "tooltip" : "Enter the port number for connecting to the POP3 server. The standard port is 995 for secure connections with SSL/TLS, or 110 for non-secure connections.", + "type" : "String" + }, { + "id" : "pop3CryptographicProtocol", + "label" : "Cryptographic protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.pop3Config.pop3CryptographicProtocol", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.smtpHost", + "label" : "SMTP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.smtpConfig.smtpHost", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "tooltip" : "Provide the address of the SMTP server used for sending emails. This server handles the delivery of your outgoing messages. (e.g., smtp.example.com)", + "type" : "String" + }, { + "id" : "data.smtpPort", + "label" : "SMTP Port", + "optional" : false, + "value" : "587", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.smtpConfig.smtpPort", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "tooltip" : "Enter the port number for connecting to the SMTP server. Typically, port 587 is used for secure connections with STARTTLS, port 465 for secure connections using SSL/TLS, and port 25 for non-secure connections.", + "type" : "String" + }, { + "id" : "smtpCryptographicProtocol", + "label" : "Cryptographic protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.smtpConfig.smtpCryptographicProtocol", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.smtpActionDiscriminator", + "label" : "SMTP action", + "value" : "sendEmailSmtp", + "group" : "smtpAction", + "binding" : { + "name" : "data.smtpActionDiscriminator", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Send Email", + "value" : "sendEmailSmtp" + } ] + }, { + "id" : "data.pop3ActionDiscriminator", + "label" : "POP3 action", + "value" : "listEmailsPop3", + "group" : "pop3Action", + "binding" : { + "name" : "data.pop3ActionDiscriminator", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Delete Email", + "value" : "deleteEmailPop3" + }, { + "name" : "List Emails", + "value" : "listEmailsPop3" + }, { + "name" : "Read Email", + "value" : "readEmailPop3" + }, { + "name" : "Search emails", + "value" : "searchEmailsPop3" + } ] + }, { + "id" : "data.imapActionDiscriminator", + "label" : "IMAP action", + "value" : "listEmailsImap", + "group" : "imapAction", + "binding" : { + "name" : "data.imapActionDiscriminator", + "type" : "zeebe:input" + }, + "condition" : { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Delete an email", + "value" : "deleteEmailImap" + }, { + "name" : "List emails", + "value" : "listEmailsImap" + }, { + "name" : "Move email", + "value" : "moveEmailImap" + }, { + "name" : "Read an email", + "value" : "readEmailImap" + }, { + "name" : "Search emails", + "value" : "searchEmailsImap" + } ] + }, { + "id" : "smtpFrom", + "label" : "From", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.from", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpTo", + "label" : "To", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.to", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpCc", + "label" : "Cc", + "optional" : true, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.cc", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpBcc", + "label" : "Bcc", + "optional" : true, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.bcc", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + "type" : "String" + }, { + "id" : "smtpSubject", + "label" : "Subject", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.subject", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Email's subject", + "type" : "String" + }, { + "id" : "smtpBody", + "label" : "Email Content", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "sendEmailSmtp", + "binding" : { + "name" : "data.smtpAction.body", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.smtpActionDiscriminator", + "equals" : "sendEmailSmtp", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "smtp", + "type" : "simple" + } ] + }, + "tooltip" : "Email's content", + "type" : "Text" + }, { + "id" : "pop3maxToBeRead", + "label" : "Maximum number of emails to be read", + "optional" : false, + "value" : "100", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsPop3", + "binding" : { + "name" : "data.pop3Action.maxToBeRead", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "listEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the maximum number of emails to be read from the specified folder. This limits the number of emails fetched to avoid performance issues with large mailboxes. The default value is set to 100.", + "type" : "String" + }, { + "id" : "pop3SortField", + "label" : "Sort emails by", + "optional" : false, + "value" : "SENT_DATE", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsPop3", + "binding" : { + "name" : "data.pop3Action.sortField", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "listEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Choose the criterion by which the listed emails should be sorted. The default sorting is by 'Sent Date'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "Sent Date", + "value" : "SENT_DATE" + }, { + "name" : "Size", + "value" : "SIZE" + } ] + }, { + "id" : "pop3SortOrder", + "label" : "Sort order", + "optional" : false, + "value" : "ASC", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsPop3", + "binding" : { + "name" : "data.pop3Action.sortOrder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "listEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Select the sort order for the emails. Choose 'ASC' for ascending order or 'DESC' for descending order. Ascending order will list older emails first, while descending order will list newer emails first. The default sort order is 'ASC'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "ASC", + "value" : "ASC" + }, { + "name" : "DESC", + "value" : "DESC" + } ] + }, { + "id" : "searchStringEmailPop3", + "label" : "Search criteria", + "optional" : true, + "feel" : "required", + "group" : "searchEmailsPop3", + "binding" : { + "name" : "data.pop3Action.criteria", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "searchEmailsPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Define the search criteria using supported keywords and syntax to filter emails. Refer to our detailed documentation for full search syntax and examples: [Email Documentation](https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/).", + "type" : "Text" + }, { + "id" : "pop3MessageIdDelete", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "deleteEmailPop3", + "binding" : { + "name" : "data.pop3Action.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "deleteEmailPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "pop3MessageIdRead", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "readEmailPop3", + "binding" : { + "name" : "data.pop3Action.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "readEmailPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "deleteOnRead", + "label" : "Delete after reading", + "optional" : false, + "value" : false, + "feel" : "optional", + "group" : "readEmailPop3", + "binding" : { + "name" : "data.pop3Action.deleteOnRead", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.pop3ActionDiscriminator", + "equals" : "readEmailPop3", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "pop3", + "type" : "simple" + } ] + }, + "tooltip" : "Enable this option if you want the email to be automatically deleted from the server after it is read. By default, this option is turned off to retain emails on the server.", + "type" : "Boolean" + }, { + "id" : "imapMaxToBeRead", + "label" : "Maximum number of emails to be read", + "optional" : false, + "value" : "100", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.maxToBeRead", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the maximum number of emails to be read from the specified folder. This limits the number of emails fetched to avoid performance issues with large mailboxes. The default value is set to 100.", + "type" : "String" + }, { + "id" : "imapListEmailsFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.listEmailsFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the folder from which you want to list emails (e.g., 'INBOX', 'Sent', 'Drafts'). If left blank, emails will be listed from the default 'INBOX' folder.", + "type" : "String" + }, { + "id" : "imapSortField", + "label" : "Sort emails by", + "optional" : false, + "value" : "RECEIVED_DATE", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.sortField", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Choose the criterion by which the listed emails should be sorted. The default sorting is by 'Received Date'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "Received Date", + "value" : "RECEIVED_DATE" + }, { + "name" : "Sent Date", + "value" : "SENT_DATE" + }, { + "name" : "Size", + "value" : "SIZE" + } ] + }, { + "id" : "imapSortOrder", + "label" : "Sort order", + "optional" : false, + "value" : "ASC", + "constraints" : { + "notEmpty" : true + }, + "group" : "listEmailsImap", + "binding" : { + "name" : "data.imapAction.sortOrder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "listEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Select the sort order for the emails. Choose 'ASC' for ascending order or 'DESC' for descending order. Ascending order will list older emails first, while descending order will list newer emails first. The default sort order is 'ASC'.", + "type" : "Dropdown", + "choices" : [ { + "name" : "ASC", + "value" : "ASC" + }, { + "name" : "DESC", + "value" : "DESC" + } ] + }, { + "id" : "searchStringEmailImap", + "label" : "Search criteria", + "optional" : true, + "feel" : "required", + "group" : "searchEmailsImap", + "binding" : { + "name" : "data.imapAction.criteria", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "searchEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Define the search criteria using supported keywords and syntax to filter emails. Refer to our detailed documentation for full search syntax and examples: [Email Documentation](https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/).", + "type" : "Text" + }, { + "id" : "searchEmailFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "searchEmailsImap", + "binding" : { + "name" : "data.imapAction.searchEmailFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "searchEmailsImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the folder in which to conduct the email search. If left blank, the search will default to the 'INBOX' folder. You may also specify subfolders using a dot-separated path (e.g., 'INBOX.Archives').", + "type" : "String" + }, { + "id" : "imapMessageIdRead", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "readEmailImap", + "binding" : { + "name" : "data.imapAction.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "readEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "readEmailFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "readEmailImap", + "binding" : { + "name" : "data.imapAction.readEmailFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "readEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the name of the folder from which you wish to read emails. If left blank, emails will be read from the default 'INBOX' folder.", + "type" : "String" + }, { + "id" : "imapMessageIdDelete", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "deleteEmailImap", + "binding" : { + "name" : "data.imapAction.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "deleteEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "deleteEmailFolder", + "label" : "Folder", + "optional" : true, + "feel" : "optional", + "group" : "deleteEmailImap", + "binding" : { + "name" : "data.imapAction.deleteEmailFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "deleteEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the name of the folder from which you want to delete emails. If left blank, the default 'INBOX' will be used. For example, you can enter 'Trash' to delete emails from the Trash folder.", + "type" : "String" + }, { + "id" : "imapMessageIdMove", + "label" : "Message ID", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "moveEmailImap", + "binding" : { + "name" : "data.imapAction.messageId", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "moveEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "The ID of the message, typically returned by a previous email task.", + "type" : "String" + }, { + "id" : "data.fromFolder", + "label" : "Source folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "moveEmailImap", + "binding" : { + "name" : "data.imapAction.fromFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "moveEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Enter the name of the folder from which the emails will be moved. This field is required. For example, enter 'INBOX' to move emails from your Inbox.", + "type" : "String" + }, { + "id" : "data.toFolder", + "label" : "Target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "moveEmailImap", + "binding" : { + "name" : "data.imapAction.toFolder", + "type" : "zeebe:input" + }, + "condition" : { + "allMatch" : [ { + "property" : "data.imapActionDiscriminator", + "equals" : "moveEmailImap", + "type" : "simple" + }, { + "property" : "protocol", + "equals" : "imap", + "type" : "simple" + } ] + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "key" : "resultVariable", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "key" : "resultExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "errorExpression", + "label" : "Error expression", + "description" : "Expression to handle errors. Details in the documentation.", + "feel" : "required", + "group" : "error", + "binding" : { + "key" : "errorExpression", + "type" : "zeebe:taskHeader" + }, + "type" : "Text" + }, { + "id" : "retryCount", + "label" : "Retries", + "description" : "Number of retries", + "value" : "3", + "feel" : "optional", + "group" : "retries", + "binding" : { + "property" : "retries", + "type" : "zeebe:taskDefinition" + }, + "type" : "String" + }, { + "id" : "retryBackoff", + "label" : "Retry backoff", + "description" : "ISO-8601 duration to wait between retries", + "value" : "PT0S", + "feel" : "optional", + "group" : "retries", + "binding" : { + "key" : "retryBackoff", + "type" : "zeebe:taskHeader" + }, + "type" : "String" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/element-templates/hybrid/hybrid-email-message-start-event-connector-hybrid.json b/connectors/email/element-templates/hybrid/hybrid-email-message-start-event-connector-hybrid.json new file mode 100644 index 0000000000..8ba2e690d5 --- /dev/null +++ b/connectors/email/element-templates/hybrid/hybrid-email-message-start-event-connector-hybrid.json @@ -0,0 +1,452 @@ +{ + "$schema" : "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "name" : "Hybrid Email Message Start Event Connector", + "id" : "io.camunda.connectors.inbound.EmailMessageStart.v1-hybrid", + "description" : "Consume emails", + "documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email", + "version" : 1, + "category" : { + "id" : "connectors", + "name" : "Connectors" + }, + "appliesTo" : [ "bpmn:StartEvent" ], + "elementType" : { + "value" : "bpmn:StartEvent", + "eventDefinition" : "bpmn:MessageEventDefinition" + }, + "groups" : [ { + "id" : "connectorType", + "label" : "Connector type" + }, { + "id" : "authentication", + "label" : "Authentication" + }, { + "id" : "protocol", + "label" : "Imap Details" + }, { + "id" : "listenerInfos", + "label" : "Listener information" + }, { + "id" : "activation", + "label" : "Activation" + }, { + "id" : "correlation", + "label" : "Correlation", + "tooltip" : "Learn more about message correlation in the documentation." + }, { + "id" : "deduplication", + "label" : "Deduplication", + "tooltip" : "Deduplication allows you to configure multiple inbound connector elements to reuse the same backend (consumer/thread/endpoint) by sharing the same deduplication ID." + }, { + "id" : "output", + "label" : "Output mapping" + } ], + "properties" : [ { + "id" : "connectorType", + "value" : "io.camunda:connector-email-inbound:1", + "group" : "connectorType", + "binding" : { + "name" : "inbound.type", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "authentication.type", + "label" : "Authentication", + "description" : "Specify the Email authentication strategy.", + "value" : "simple", + "group" : "authentication", + "binding" : { + "name" : "authentication.type", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Simple", + "value" : "simple" + } ] + }, { + "id" : "authentication.simpleAuthenticationUsername", + "label" : "Username", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.username", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + "type" : "String" + }, { + "id" : "authentication.simpleAuthenticationPassword", + "label" : "Email password", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "authentication", + "binding" : { + "name" : "authentication.password", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "authentication.type", + "equals" : "simple", + "type" : "simple" + }, + "tooltip" : "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + "type" : "String" + }, { + "id" : "data.imapHost", + "label" : "IMAP Host", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapHost", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + "type" : "String" + }, { + "id" : "data.imapPort", + "label" : "IMAP Port", + "optional" : false, + "value" : "993", + "constraints" : { + "notEmpty" : true + }, + "feel" : "optional", + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapPort", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + "type" : "String" + }, { + "id" : "imapCryptographicProtocol", + "label" : "Encryption protocol", + "optional" : false, + "value" : "TLS", + "constraints" : { + "notEmpty" : true + }, + "group" : "protocol", + "binding" : { + "name" : "data.imapConfig.imapCryptographicProtocol", + "type" : "zeebe:property" + }, + "tooltip" : "Select the encryption protocol for email security.", + "type" : "Dropdown", + "choices" : [ { + "name" : "TLS", + "value" : "TLS" + }, { + "name" : "None", + "value" : "NONE" + }, { + "name" : "SSL", + "value" : "SSL" + } ] + }, { + "id" : "data.folderToListen", + "label" : "Folder to listen", + "optional" : true, + "feel" : "optional", + "group" : "listenerInfos", + "binding" : { + "name" : "data.folderToListen", + "type" : "zeebe:property" + }, + "tooltip" : "Enter the names of the folders you wish to monitor, separated by commas, for new emails or changes (e.g., 'INBOX, Sent, Archive'). If left blank, the listener will default to monitoring the 'INBOX' folder.", + "type" : "String" + }, { + "id" : "data.initialPollingConfig", + "label" : "Sync strategy", + "optional" : false, + "value" : "UNSEEN", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.initialPollingConfig", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired polling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Unseen emails will be sync", + "value" : "UNSEEN" + }, { + "name" : "No initial sync. Only new emails", + "value" : "NONE" + }, { + "name" : "All emails will be sync", + "value" : "ALL" + } ] + }, { + "id" : "data.handlingStrategy", + "label" : "Handling strategy", + "optional" : false, + "value" : "READ", + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.handlingStrategy", + "type" : "zeebe:property" + }, + "tooltip" : "Chose the desired handling strategy", + "type" : "Dropdown", + "choices" : [ { + "name" : "Mark as read after processing", + "value" : "READ" + }, { + "name" : "Do nothing", + "value" : "NO_HANDLING" + }, { + "name" : "Delete after processing", + "value" : "DELETE" + }, { + "name" : "Move to another folder after processing", + "value" : "MOVE" + } ] + }, { + "id" : "data.targetFolder", + "label" : "Choose the target folder", + "optional" : false, + "constraints" : { + "notEmpty" : true + }, + "group" : "listenerInfos", + "binding" : { + "name" : "data.targetFolder", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "data.handlingStrategy", + "equals" : "MOVE", + "type" : "simple" + }, + "tooltip" : "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + "type" : "String" + }, { + "id" : "activationCondition", + "label" : "Activation condition", + "description" : "Condition under which the Connector triggers. Leave empty to catch all events", + "optional" : true, + "feel" : "required", + "group" : "activation", + "binding" : { + "name" : "activationCondition", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "consumeUnmatchedEvents", + "label" : "Consume unmatched events", + "value" : true, + "group" : "activation", + "binding" : { + "name" : "consumeUnmatchedEvents", + "type" : "zeebe:property" + }, + "tooltip" : "Unmatched events are rejected by default, allowing the upstream service to handle the error. Check this box to consume unmatched events and return a success response", + "type" : "Boolean" + }, { + "id" : "correlationRequired", + "label" : "Subprocess correlation required", + "description" : "Indicates whether correlation is required. This is needed for event-based subprocess message start events", + "value" : "notRequired", + "group" : "correlation", + "binding" : { + "name" : "correlationRequired", + "type" : "zeebe:property" + }, + "type" : "Dropdown", + "choices" : [ { + "name" : "Correlation not required", + "value" : "notRequired" + }, { + "name" : "Correlation required", + "value" : "required" + } ] + }, { + "id" : "correlationKeyProcess", + "label" : "Correlation key (process)", + "description" : "Sets up the correlation key from process variables", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKey", + "type" : "bpmn:Message#zeebe:subscription#property" + }, + "condition" : { + "property" : "correlationRequired", + "equals" : "required", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "correlationKeyPayload", + "label" : "Correlation key (payload)", + "description" : "Extracts the correlation key from the incoming message payload", + "constraints" : { + "notEmpty" : true + }, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "correlationKeyExpression", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "correlationRequired", + "equals" : "required", + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "messageTtl", + "label" : "Message TTL", + "description" : "Time-to-live for the message in the broker (ISO-8601 duration)", + "optional" : true, + "constraints" : { + "notEmpty" : false, + "pattern" : { + "value" : "^(PT.*|)$", + "message" : "must be an ISO-8601 duration" + } + }, + "feel" : "optional", + "group" : "correlation", + "binding" : { + "name" : "messageTtl", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageIdExpression", + "label" : "Message ID expression", + "description" : "Expression to extract unique identifier of a message", + "optional" : true, + "feel" : "required", + "group" : "correlation", + "binding" : { + "name" : "messageIdExpression", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "messageNameUuid", + "generatedValue" : { + "type" : "uuid" + }, + "group" : "correlation", + "binding" : { + "name" : "name", + "type" : "bpmn:Message#property" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeManualFlag", + "label" : "Manual mode", + "description" : "By default, similar connectors receive the same deduplication ID. Customize by activating manual mode", + "value" : false, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationModeManualFlag", + "type" : "zeebe:property" + }, + "type" : "Boolean" + }, { + "id" : "deduplicationId", + "label" : "Deduplication ID", + "constraints" : { + "notEmpty" : true, + "pattern" : { + "value" : "^[a-zA-Z0-9_-]+$", + "message" : "can only contain alphanumeric characters, dashes, and underscores" + } + }, + "group" : "deduplication", + "binding" : { + "name" : "deduplicationId", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationModeManualFlag", + "equals" : true, + "type" : "simple" + }, + "type" : "String" + }, { + "id" : "deduplicationModeManual", + "value" : "MANUAL", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : true, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "deduplicationModeAuto", + "value" : "AUTO", + "group" : "deduplication", + "binding" : { + "name" : "deduplicationMode", + "type" : "zeebe:property" + }, + "condition" : { + "property" : "deduplicationId", + "isActive" : false, + "type" : "simple" + }, + "type" : "Hidden" + }, { + "id" : "resultVariable", + "label" : "Result variable", + "description" : "Name of variable to store the response in", + "group" : "output", + "binding" : { + "name" : "resultVariable", + "type" : "zeebe:property" + }, + "type" : "String" + }, { + "id" : "resultExpression", + "label" : "Result expression", + "description" : "Expression to map the response into process variables", + "feel" : "required", + "group" : "output", + "binding" : { + "name" : "resultExpression", + "type" : "zeebe:property" + }, + "type" : "Text" + } ], + "icon" : { + "contents" : "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzkwXzI0MjApIj4KPHBhdGggZD0iTTguMzM4MzUgOS45NTM2NUwxMC4zODk0IDEyLjAxMDRMOC4zMzI2MiAxNC4wNjcyTDkuMTQ2MTYgMTQuODc1TDEyLjAxMDcgMTIuMDEwNEw5LjE0NjE2IDkuMTQ1ODNMOC4zMzgzNSA5Ljk1MzY1WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyLjM0ODggOS45NTM2NUwxNC4zOTk4IDEyLjAxMDRMMTIuMzQzIDE0LjA2NzJMMTMuMTU2NiAxNC44NzVMMTYuMDIxMiAxMi4wMTA0TDEzLjE1NjYgOS4xNDU4M0wxMi4zNDg4IDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNMy45NzIgMTEuNDM3NUgxLjEyNTMzVjIuNzkyMTlMNy42NzM3NiA3LjMyMzk2QzcuNzY5NjcgNy4zOTA0OSA3Ljg4MzYgNy40MjYxNCA4LjAwMDMyIDcuNDI2MTRDOC4xMTcwNSA3LjQyNjE0IDguMjMwOTggNy4zOTA0OSA4LjMyNjg5IDcuMzIzOTZMMTQuODc1MyAyLjc5MjE5VjhIMTYuMDIxMlYyLjI3MDgzQzE2LjAyMTIgMS45NjY5NCAxNS45MDA0IDEuNjc1NDkgMTUuNjg1NiAxLjQ2MDYxQzE1LjQ3MDcgMS4yNDU3MiAxNS4xNzkyIDEuMTI1IDE0Ljg3NTMgMS4xMjVIMS4xMjUzM0MwLjgyMTQzMiAxLjEyNSAwLjUyOTk4NCAxLjI0NTcyIDAuMzE1MDk5IDEuNDYwNjFDMC4xMDAyMTQgMS42NzU0OSAtMC4wMjA1MDc4IDEuOTY2OTQgLTAuMDIwNTA3OCAyLjI3MDgzVjExLjQzNzVDLTAuMDIwNTA3OCAxMS43NDE0IDAuMTAwMjE0IDEyLjAzMjggMC4zMTUwOTkgMTIuMjQ3N0MwLjUyOTk4NCAxMi40NjI2IDAuODIxNDMyIDEyLjU4MzMgMS4xMjUzMyAxMi41ODMzSDMuOTcyVjExLjQzNzVaTTEzLjYxNDkgMi4yNzA4M0w4LjAwMDMyIDYuMTU1MjFMMi4zODU3NCAyLjI3MDgzSDEzLjYxNDlaIiBmaWxsPSIjRkM1RDBEIi8+CjxwYXRoIGQ9Ik00LjI4MjEgOS45NTM2NUw2LjMzMzE0IDEyLjAxMDRMNC4yNzYzNyAxNC4wNjcyTDUuMDg5OTEgMTQuODc1TDcuOTU0NDkgMTIuMDEwNEw1LjA4OTkxIDkuMTQ1ODNMNC4yODIxIDkuOTUzNjVaIiBmaWxsPSJibGFjayIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzkwXzI0MjAiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==" + } +} \ No newline at end of file diff --git a/connectors/email/pom.xml b/connectors/email/pom.xml new file mode 100644 index 0000000000..bd59226376 --- /dev/null +++ b/connectors/email/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + io.camunda.connector + connectors-parent + 8.6.0-SNAPSHOT + ../pom.xml + + + connector-email + connector-email + Camunda Email Connector + jar + + + + 21 + 21 + UTF-8 + + + + + io.camunda.connector + element-template-generator-core + + + org.eclipse.angus + angus-mail + 2.0.3 + + + org.wiremock + wiremock-standalone + + + + + + + io.camunda.connector + element-template-generator-maven-plugin + ${project.version} + + + + io.camunda.connector.email.outbound.EmailConnectorFunction + + + io.camunda.connectors.email.v1 + email-outbound-connector.json + + + true + + + io.camunda.connector.email.inbound.EmailConnectorExecutable + + + io.camunda.connectors.inbound.EmailMessageStartEvent.v1 + email-inbound-connector-start-event.json + + + io.camunda.connectors.inbound.EmailIntermediate.v1 + email-inbound-connector-intermediate.json + + + io.camunda.connectors.inbound.EmailBoundary.v1 + email-inbound-connector-boundary.json + + + true + + true + true + + + + + + + + + \ No newline at end of file diff --git a/connectors/email/src/main/java/io/camunda/connector/email/authentication/Authentication.java b/connectors/email/src/main/java/io/camunda/connector/email/authentication/Authentication.java new file mode 100644 index 0000000000..1fb91b2086 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/authentication/Authentication.java @@ -0,0 +1,24 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.authentication; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.camunda.connector.generator.java.annotation.TemplateDiscriminatorProperty; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "type", + defaultImpl = SimpleAuthentication.class) +@JsonSubTypes({@JsonSubTypes.Type(value = SimpleAuthentication.class, name = "simple")}) +@TemplateDiscriminatorProperty( + label = "Authentication", + group = "authentication", + name = "type", + defaultValue = "simple", + description = "Specify the Email authentication strategy.") +public sealed interface Authentication permits SimpleAuthentication {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/authentication/SimpleAuthentication.java b/connectors/email/src/main/java/io/camunda/connector/email/authentication/SimpleAuthentication.java new file mode 100644 index 0000000000..cb23577be9 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/authentication/SimpleAuthentication.java @@ -0,0 +1,33 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.authentication; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.constraints.NotBlank; + +@TemplateSubType(id = "simple", label = "Simple") +public record SimpleAuthentication( + @TemplateProperty( + group = "authentication", + label = "Username", + tooltip = + "Enter your full email address (e.g., user@example.com) or the username provided by your email service. This is used to authenticate your access to the mail server.", + id = "simpleAuthenticationUsername") + @NotBlank + String username, + @TemplateProperty( + group = "authentication", + label = "Email password", + feel = Property.FeelMode.disabled, + tooltip = + "Enter the password associated with your email account. Keep your password secure and do not share it with others.", + id = "simpleAuthenticationPassword") + @NotBlank + String password) + implements Authentication {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/EmailActionExecutor.java b/connectors/email/src/main/java/io/camunda/connector/email/client/EmailActionExecutor.java new file mode 100644 index 0000000000..afde1bb8d7 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/EmailActionExecutor.java @@ -0,0 +1,13 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client; + +import io.camunda.connector.email.outbound.model.EmailRequest; + +public interface EmailActionExecutor { + Object execute(EmailRequest emailRequest); +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/EmailListener.java b/connectors/email/src/main/java/io/camunda/connector/email/client/EmailListener.java new file mode 100644 index 0000000000..969260dd6d --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/EmailListener.java @@ -0,0 +1,15 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client; + +import io.camunda.connector.api.inbound.InboundConnectorContext; + +public interface EmailListener { + void startListener(InboundConnectorContext context); + + void stopListener(); +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/Email.java b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/Email.java new file mode 100644 index 0000000000..6819ca19cc --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/Email.java @@ -0,0 +1,21 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import java.time.OffsetDateTime; +import java.util.*; + +public record Email( + EmailBody body, + String messageId, + String subject, + List from, + List to, + List cc, + OffsetDateTime sentAt, + OffsetDateTime receivedAt, + Integer size) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailAttachment.java b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailAttachment.java new file mode 100644 index 0000000000..484237df8f --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailAttachment.java @@ -0,0 +1,11 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import java.io.InputStream; + +public record EmailAttachment(InputStream inputStream, String name, String contentType) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailBody.java b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailBody.java new file mode 100644 index 0000000000..cd22bc8e5b --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/EmailBody.java @@ -0,0 +1,48 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import java.util.ArrayList; +import java.util.List; + +public record EmailBody( + String bodyAsPlainText, String bodyAsHtml, List attachments) { + + public static EmailBodyBuilder createBuilder() { + return new EmailBodyBuilder(); + } + + public static final class EmailBodyBuilder { + private final List attachments = new ArrayList<>(); + private String bodyAsPlainText; + private String bodyAsHtml; + + public EmailBodyBuilder withBodyAsPlainText(String bodyAsPlainText) { + this.bodyAsPlainText = bodyAsPlainText; + return this; + } + + public EmailBodyBuilder withBodyAsHtml(String bodyAsHtml) { + this.bodyAsHtml = bodyAsHtml; + return this; + } + + public EmailBodyBuilder addAttachment(EmailAttachment attachment) { + this.attachments.add(attachment); + return this; + } + + public EmailBodyBuilder withAttachments(List attachments) { + this.attachments.addAll(attachments); + return this; + } + + public EmailBody build() { + return new EmailBody(bodyAsPlainText, bodyAsHtml, attachments); + } + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailActionExecutor.java b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailActionExecutor.java new file mode 100644 index 0000000000..7cdc4e5d10 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailActionExecutor.java @@ -0,0 +1,340 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.camunda.connector.email.authentication.Authentication; +import io.camunda.connector.email.client.EmailActionExecutor; +import io.camunda.connector.email.outbound.model.EmailRequest; +import io.camunda.connector.email.outbound.protocols.Protocol; +import io.camunda.connector.email.outbound.protocols.actions.*; +import io.camunda.connector.email.response.*; +import jakarta.mail.*; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.search.*; +import java.util.*; + +public class JakartaEmailActionExecutor implements EmailActionExecutor { + + private final JakartaUtils jakartaUtils; + private final ObjectMapper objectMapper; + + private JakartaEmailActionExecutor(JakartaUtils jakartaUtils, ObjectMapper objectMapper) { + this.jakartaUtils = jakartaUtils; + this.objectMapper = objectMapper; + } + + public static JakartaEmailActionExecutor create( + JakartaUtils sessionFactory, ObjectMapper objectMapper) { + return new JakartaEmailActionExecutor(sessionFactory, objectMapper); + } + + public Object execute(EmailRequest emailRequest) { + Authentication authentication = emailRequest.authentication(); + Protocol protocol = emailRequest.data(); + Action action = protocol.getProtocolAction(); + Session session = jakartaUtils.createSession(protocol.getConfiguration()); + return switch (action) { + case SmtpSendEmail smtpSendEmail -> smtpSendEmail(smtpSendEmail, authentication, session); + case ImapMoveEmail imapMoveEmail -> imapMoveEmails(imapMoveEmail, authentication, session); + case ImapListEmails imapListEmails -> imapListEmails(imapListEmails, authentication, session); + case ImapDeleteEmail imapDeleteEmail -> + imapDeleteEmail(imapDeleteEmail, authentication, session); + case ImapSearchEmails imapSearchEmails -> + imapSearchEmails(imapSearchEmails, authentication, session); + case ImapReadEmail imapReadEmail -> imapReadEmail(imapReadEmail, authentication, session); + case Pop3DeleteEmail pop3DeleteEmail -> + pop3DeleteEmail(pop3DeleteEmail, authentication, session); + case Pop3ListEmails pop3ListEmails -> pop3ListEmails(pop3ListEmails, authentication, session); + case Pop3ReadEmail pop3ReadEmail -> pop3ReadEmail(pop3ReadEmail, authentication, session); + case Pop3SearchEmails pop3SearchEmails -> + pop3SearchEmails(pop3SearchEmails, authentication, session); + }; + } + + private List imapSearchEmails( + ImapSearchEmails imapSearchEmails, Authentication authentication, Session session) { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + Folder defaultFolder = store.getDefaultFolder(); + String targetFolder = imapSearchEmails.searchEmailFolder(); + try (Folder imapFolder = this.jakartaUtils.findImapFolder(defaultFolder, targetFolder)) { + return searchEmails(imapFolder, imapSearchEmails.criteria()); + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private ReadEmailResponse imapReadEmail( + ImapReadEmail imapReadEmail, Authentication authentication, Session session) { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + Folder defaultFolder = store.getDefaultFolder(); + String targetFolder = imapReadEmail.readEmailFolder(); + try (Folder imapFolder = this.jakartaUtils.findImapFolder(defaultFolder, targetFolder)) { + imapFolder.open(Folder.READ_ONLY); + Message[] messages = imapFolder.search(new MessageIDTerm(imapReadEmail.messageId())); + return Arrays.stream(messages) + .findFirst() + .map(this.jakartaUtils::createEmail) + .map( + email -> + new ReadEmailResponse( + email.messageId(), + email.from(), + email.subject(), + email.size(), + email.body().bodyAsPlainText(), + email.body().bodyAsHtml())) + .orElseThrow(() -> new MessagingException("Could not find an email ID")); + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private DeleteEmailResponse imapDeleteEmail( + ImapDeleteEmail imapDeleteEmail, Authentication authentication, Session session) { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + Folder defaultFolder = store.getDefaultFolder(); + String targetFolder = imapDeleteEmail.deleteEmailFolder(); + try (Folder folder = this.jakartaUtils.findImapFolder(defaultFolder, targetFolder)) { + return deleteEmail(folder, imapDeleteEmail.messageId()); + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private MoveEmailResponse imapMoveEmails( + ImapMoveEmail imapMoveEmail, Authentication authentication, Session session) { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + Folder rootFolder = store.getDefaultFolder(); + String fromFolder = imapMoveEmail.fromFolder(); + String toFolder = imapMoveEmail.toFolder(); + Folder sourceImapFolder = this.jakartaUtils.findImapFolder(rootFolder, fromFolder); + if (!sourceImapFolder.exists()) throw new MessagingException("Source folder does not exist"); + sourceImapFolder.open(Folder.READ_WRITE); + Folder targetImapFolder = + store.getFolder( + String.join(String.valueOf(rootFolder.getSeparator()), toFolder.split("\\."))); + if (!targetImapFolder.exists()) targetImapFolder.create(Folder.HOLDS_MESSAGES); + targetImapFolder.open(Folder.READ_WRITE); + + Message[] messages = sourceImapFolder.search(new MessageIDTerm(imapMoveEmail.messageId())); + Message message = + Arrays.stream(messages) + .findFirst() + .orElseThrow( + () -> + new MessagingException( + "Email with messageId %s does not exist" + .formatted(imapMoveEmail.messageId()))); + sourceImapFolder.copyMessages(new Message[] {message}, targetImapFolder); + this.jakartaUtils.markAsDeleted(message); + sourceImapFolder.close(); + targetImapFolder.close(); + return new MoveEmailResponse( + imapMoveEmail.messageId(), imapMoveEmail.fromFolder(), imapMoveEmail.toFolder()); + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private List imapListEmails( + ImapListEmails imapListEmails, Authentication authentication, Session session) { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + Folder rootFolder = store.getDefaultFolder(); + String targetFolder = imapListEmails.listEmailsFolder(); + try (Folder imapFolder = this.jakartaUtils.findImapFolder(rootFolder, targetFolder)) { + imapFolder.open(Folder.READ_ONLY); + return Arrays.stream(imapFolder.getMessages()) + .map(this.jakartaUtils::createBodylessEmail) + .sorted( + this.jakartaUtils.retrieveEmailComparator( + imapListEmails.sortField(), imapListEmails.sortOrder())) + .map( + email -> + new ListEmailsResponse( + email.messageId(), email.from(), email.subject(), email.size())) + .limit(imapListEmails.maxToBeRead()) + .toList(); + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private DeleteEmailResponse pop3DeleteEmail( + Pop3DeleteEmail pop3DeleteEmail, Authentication authentication, Session session) { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + try (Folder folder = store.getFolder("INBOX")) { + return deleteEmail(folder, pop3DeleteEmail.messageId()); + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private ReadEmailResponse pop3ReadEmail( + Pop3ReadEmail pop3ReadEmail, Authentication authentication, Session session) { + try { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + try (Folder folder = store.getFolder("INBOX")) { + folder.open(Folder.READ_WRITE); + Message[] messages = folder.search(new MessageIDTerm(pop3ReadEmail.messageId())); + return Arrays.stream(messages) + .findFirst() + .map(this.jakartaUtils::createEmail) + .map( + email -> + new ReadEmailResponse( + email.messageId(), + email.from(), + email.subject(), + email.size(), + email.body().bodyAsPlainText(), + email.body().bodyAsHtml())) + .orElseThrow(() -> new MessagingException("No emails have been found with this ID")); + } + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private List pop3ListEmails( + Pop3ListEmails pop3ListEmails, Authentication authentication, Session session) { + try { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + try (Folder folder = store.getFolder("INBOX")) { + folder.open(Folder.READ_ONLY); + return Arrays.stream(folder.getMessages()) + .map(this.jakartaUtils::createBodylessEmail) + .sorted( + this.jakartaUtils.retrieveEmailComparator( + pop3ListEmails.sortField(), pop3ListEmails.sortOrder())) + .map( + email -> + new ListEmailsResponse( + email.messageId(), email.from(), email.subject(), email.size())) + .limit(pop3ListEmails.maxToBeRead()) + .toList(); + } + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private List pop3SearchEmails( + Pop3SearchEmails pop3SearchEmails, Authentication authentication, Session session) { + try (Store store = session.getStore()) { + this.jakartaUtils.connectStore(store, authentication); + try (Folder folder = store.getFolder("INBOX")) { + return searchEmails(folder, pop3SearchEmails.criteria()); + } + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private SendEmailResponse smtpSendEmail( + SmtpSendEmail smtpSendEmail, Authentication authentication, Session session) { + try { + Optional from = createParsedInternetAddresses(smtpSendEmail.from()); + Optional to = createParsedInternetAddresses(smtpSendEmail.to()); + Optional cc = createParsedInternetAddresses(smtpSendEmail.cc()); + Optional bcc = createParsedInternetAddresses(smtpSendEmail.bcc()); + Message message = new MimeMessage(session); + if (from.isPresent()) message.addFrom(from.get()); + if (to.isPresent()) message.setRecipients(Message.RecipientType.TO, to.get()); + if (cc.isPresent()) message.setRecipients(Message.RecipientType.CC, cc.get()); + if (bcc.isPresent()) message.setRecipients(Message.RecipientType.BCC, bcc.get()); + message.setSubject(smtpSendEmail.subject()); + message.setText(smtpSendEmail.body()); + try (Transport transport = session.getTransport()) { + this.jakartaUtils.connectTransport(transport, authentication); + transport.sendMessage(message, message.getAllRecipients()); + } + return new SendEmailResponse(smtpSendEmail.subject(), true); + } catch (SendFailedException e) { + return new SendEmailResponse(smtpSendEmail.subject(), false); + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private SearchTerm createSearchTerms(JsonNode jsonNode) throws AddressException { + List searchTerms = new ArrayList<>(); + if (jsonNode.has("operator")) { + JsonNode criteriaArray = jsonNode.get("criteria"); + for (JsonNode criteria : criteriaArray) { + searchTerms.add(createSearchTerms(criteria)); + } + } else { + return switch (SearchCriteria.valueOf(jsonNode.get("field").asText())) { + case FROM -> new FromTerm(new InternetAddress(jsonNode.get("value").asText())); + case SUBJECT -> new SubjectTerm(jsonNode.get("value").asText()); + case BODY -> new BodyTerm(jsonNode.get("value").asText()); + }; + } + return switch (SearchOperator.valueOf(jsonNode.get("operator").asText())) { + case AND -> new AndTerm(searchTerms.toArray(new SearchTerm[0])); + case OR -> new OrTerm(searchTerms.toArray(new SearchTerm[0])); + }; + } + + private List searchEmails(Folder folder, Object criteria) + throws MessagingException { + folder.open(Folder.READ_ONLY); + JsonNode jsonNode = this.objectMapper.convertValue(criteria, JsonNode.class); + SearchTerm searchTerm = createSearchTerms(jsonNode); + return Arrays.stream(folder.search(searchTerm)) + .map(this.jakartaUtils::createBodylessEmail) + .map(email -> new SearchEmailsResponse(email.messageId(), email.subject())) + .toList(); + } + + private DeleteEmailResponse deleteEmail(Folder folder, String messageId) + throws MessagingException { + folder.open(Folder.READ_WRITE); + Message[] messages = folder.search(new MessageIDTerm(messageId)); + Message message = + Arrays.stream(messages) + .findFirst() + .orElseThrow(() -> new MessagingException("No emails have been found with this ID")); + this.jakartaUtils.markAsDeleted(message); + return new DeleteEmailResponse(messageId, true); + } + + private Optional createParsedInternetAddresses(Object object) + throws AddressException { + if (Objects.isNull(object)) { + return Optional.empty(); + } + return Optional.of( + switch (object) { + case List list -> + InternetAddress.parse(String.join(",", list.stream().map(Object::toString).toList())); + case String string -> InternetAddress.parse(string); + default -> + throw new IllegalStateException( + "Unexpected value: " + object + ". List or String was expected"); + }); + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailListener.java b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailListener.java new file mode 100644 index 0000000000..b18159c80d --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaEmailListener.java @@ -0,0 +1,213 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import io.camunda.connector.api.inbound.CorrelationFailureHandlingStrategy; +import io.camunda.connector.api.inbound.CorrelationResult; +import io.camunda.connector.api.inbound.InboundConnectorContext; +import io.camunda.connector.email.authentication.Authentication; +import io.camunda.connector.email.client.EmailListener; +import io.camunda.connector.email.inbound.model.*; +import io.camunda.connector.email.response.ReadEmailResponse; +import jakarta.mail.*; +import jakarta.mail.event.MessageChangedEvent; +import jakarta.mail.event.MessageCountEvent; +import jakarta.mail.event.MessageCountListener; +import jakarta.mail.search.FlagTerm; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.eclipse.angus.mail.imap.IMAPFolder; +import org.eclipse.angus.mail.imap.IdleManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JakartaEmailListener implements EmailListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(JakartaEmailListener.class); + + private final JakartaUtils jakartaUtils; + private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor(); + + private IdleManager idleManager; + private Store store; + private List imapFolders; + + public JakartaEmailListener(JakartaUtils jakartaUtils) { + this.jakartaUtils = jakartaUtils; + } + + public static JakartaEmailListener create(JakartaUtils sessionFactory) { + return new JakartaEmailListener(sessionFactory); + } + + public void startListener(InboundConnectorContext context) { + EmailInboundConnectorProperties emailInboundConnectorProperties = + context.bindProperties(EmailInboundConnectorProperties.class); + Authentication authentication = emailInboundConnectorProperties.authentication(); + Session session = + this.jakartaUtils.createSession(emailInboundConnectorProperties.data().imapConfig()); + try { + this.store = session.getStore(); + this.imapFolders = new ArrayList<>(); + this.idleManager = new IdleManager(session, this.executorService); + this.jakartaUtils.connectStore(this.store, authentication); + List inboxes = + createInboxList(emailInboundConnectorProperties.data().folderToListen()); + for (String inbox : inboxes) { + IMAPFolder folder = + (IMAPFolder) this.jakartaUtils.findImapFolder(store.getDefaultFolder(), inbox); + EmailListenerConfig emailListenerConfig = emailInboundConnectorProperties.data(); + folder.open(Folder.READ_WRITE); + folder.addMessageChangedListener( + event -> processChangedEvent(event, context, emailListenerConfig)); + folder.addMessageCountListener( + new MessageCountListener() { + @Override + public void messagesAdded(MessageCountEvent event) { + processNewEvent(event, context, emailListenerConfig); + } + + @Override + public void messagesRemoved(MessageCountEvent event) {} + }); + switch (emailListenerConfig.initialPollingConfig()) { + case UNSEEN -> pollUnseenAndProcess(folder, emailListenerConfig, context); + case ALL -> pollAllAndProcess(folder, emailListenerConfig, context); + case NONE -> {} + } + this.imapFolders.add(folder); + idleManager.watch(folder); + } + } catch (MessagingException | IOException e) { + LOGGER.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + + private void processChangedEvent( + MessageChangedEvent event, + InboundConnectorContext connectorContext, + EmailListenerConfig emailListenerConfig) { + IMAPFolder imapFolder = (IMAPFolder) event.getSource(); + Message message = event.getMessage(); + try { + if (!message.isSet(Flags.Flag.SEEN)) + processMail(message, connectorContext, emailListenerConfig); + idleManager.watch(imapFolder); + } catch (MessagingException ex) { + throw new RuntimeException(ex); + } + } + + private void pollAllAndProcess( + IMAPFolder folder, EmailListenerConfig emailListenerConfig, InboundConnectorContext context) { + try { + Message[] messages = folder.getMessages(); + Arrays.stream(messages) + .forEach(message -> processMail(message, context, emailListenerConfig)); + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private void pollUnseenAndProcess( + IMAPFolder folder, EmailListenerConfig emailListenerConfig, InboundConnectorContext context) { + try { + FlagTerm unseenFlagTerm = new FlagTerm(new Flags(Flags.Flag.SEEN), false); + Message[] unseenMessages = folder.search(unseenFlagTerm); + Arrays.stream(unseenMessages) + .forEach(message -> processMail(message, context, emailListenerConfig)); + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private void processNewEvent( + MessageCountEvent event, + InboundConnectorContext connectorContext, + EmailListenerConfig emailListenerConfig) { + IMAPFolder imapFolder = (IMAPFolder) event.getSource(); + for (Message message : event.getMessages()) { + processMail(message, connectorContext, emailListenerConfig); + } + try { + idleManager.watch(imapFolder); + } catch (MessagingException ex) { + throw new RuntimeException(ex); + } + } + + private void processMail( + Message message, + InboundConnectorContext connectorContext, + EmailListenerConfig emailListenerConfig) { + + CorrelationResult correlationResult = this.correlateEmail(message, connectorContext); + + switch (correlationResult) { + case CorrelationResult.Failure failure -> { + switch (failure.handlingStrategy()) { + case CorrelationFailureHandlingStrategy.ForwardErrorToUpstream __ -> {} + case CorrelationFailureHandlingStrategy.Ignore __ -> + executePostProcess(message, emailListenerConfig); + } + } + case CorrelationResult.Success __ -> executePostProcess(message, emailListenerConfig); + } + } + + private void executePostProcess(Message message, EmailListenerConfig emailListenerConfig) { + switch (emailListenerConfig.handlingStrategy()) { + case READ -> this.jakartaUtils.markAsSeen(message); + case DELETE -> this.jakartaUtils.markAsDeleted(message); + case NO_HANDLING -> {} + case MOVE -> + this.jakartaUtils.moveMessage(this.store, message, emailListenerConfig.targetFolder()); + } + } + + private CorrelationResult correlateEmail( + Message message, InboundConnectorContext connectorContext) { + Email email = this.jakartaUtils.createEmail(message); + return connectorContext.correlateWithResult( + new ReadEmailResponse( + email.messageId(), + email.from(), + email.subject(), + email.size(), + email.body().bodyAsPlainText(), + email.body().bodyAsHtml())); + } + + private List createInboxList(Object folderToListen) { + return switch (folderToListen) { + case null -> List.of(""); + case List list -> list.stream().map(Object::toString).toList(); + case String string -> List.of(string.split(",")); + default -> + throw new IllegalStateException( + "Unexpected value: " + folderToListen + ". List or String was expected"); + }; + } + + public void stopListener() { + try { + this.idleManager.stop(); + for (IMAPFolder folder : this.imapFolders) { + folder.close(); + } + this.store.close(); + } catch (MessagingException e) { + LOGGER.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaUtils.java b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaUtils.java new file mode 100644 index 0000000000..d92f40377f --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/client/jakarta/JakartaUtils.java @@ -0,0 +1,322 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import io.camunda.connector.email.authentication.Authentication; +import io.camunda.connector.email.authentication.SimpleAuthentication; +import io.camunda.connector.email.config.Configuration; +import io.camunda.connector.email.config.ImapConfig; +import io.camunda.connector.email.config.Pop3Config; +import io.camunda.connector.email.config.SmtpConfig; +import io.camunda.connector.email.outbound.protocols.actions.SortFieldImap; +import io.camunda.connector.email.outbound.protocols.actions.SortFieldPop3; +import io.camunda.connector.email.outbound.protocols.actions.SortOrder; +import jakarta.mail.*; +import jakarta.mail.internet.MimeMultipart; +import jakarta.validation.constraints.NotNull; +import java.io.IOException; +import java.io.InputStream; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JakartaUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(JakartaUtils.class); + + public Session createSession(Configuration configuration) { + return Session.getInstance( + switch (configuration) { + case ImapConfig imap -> createProperties(imap); + case Pop3Config pop3 -> createProperties(pop3); + case SmtpConfig smtp -> createProperties(smtp); + }); + } + + public void connectStore(Store store, Authentication authentication) throws MessagingException { + switch (authentication) { + case SimpleAuthentication simpleAuthentication -> + store.connect(simpleAuthentication.username(), simpleAuthentication.password()); + } + } + + public void connectTransport(Transport transport, Authentication authentication) + throws MessagingException { + switch (authentication) { + case SimpleAuthentication simpleAuthentication -> + transport.connect(simpleAuthentication.username(), simpleAuthentication.password()); + } + } + + public void markAsDeleted(Message message) { + try { + message.setFlag(Flags.Flag.DELETED, true); + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + public void markAsSeen(Message message) { + try { + message.setFlag(Flags.Flag.SEEN, true); + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + private Properties createProperties(SmtpConfig smtp) { + Properties properties = new Properties(); + properties.put("mail.transport.protocol", "smtp"); + properties.put("mail.smtp.host", smtp.smtpHost()); + properties.put("mail.smtp.port", smtp.smtpPort().toString()); + properties.put("mail.smtp.auth", true); + switch (smtp.smtpCryptographicProtocol()) { + case NONE -> {} + case TLS -> properties.put("mail.smtp.starttls.enable", true); + case SSL -> properties.put("mail.smtp.ssl.enable", true); + } + return properties; + } + + private Properties createProperties(Pop3Config pop3) { + Properties properties = new Properties(); + + switch (pop3.pop3CryptographicProtocol()) { + case NONE -> { + properties.put("mail.store.protocol", "pop3"); + properties.put("mail.pop3.host", pop3.pop3Host()); + properties.put("mail.pop3.port", pop3.pop3Port().toString()); + properties.put("mail.pop3.auth", true); + } + case TLS -> { + properties.put("mail.store.protocol", "pop3s"); + properties.put("mail.pop3s.host", pop3.pop3Host()); + properties.put("mail.pop3s.port", pop3.pop3Port().toString()); + properties.put("mail.pop3s.auth", true); + properties.put("mail.pop3s.starttls.enable", true); + } + case SSL -> { + properties.put("mail.store.protocol", "pop3s"); + properties.put("mail.pop3s.host", pop3.pop3Host()); + properties.put("mail.pop3s.port", pop3.pop3Port().toString()); + properties.put("mail.pop3s.auth", true); + properties.put("mail.pop3s.ssl.enable", true); + } + } + return properties; + } + + private Properties createProperties(ImapConfig imap) { + Properties properties = new Properties(); + + switch (imap.imapCryptographicProtocol()) { + case NONE -> { + properties.put("mail.store.protocol", "imap"); + properties.put("mail.imap.host", imap.imapHost()); + properties.put("mail.imap.port", imap.imapPort().toString()); + properties.put("mail.imap.auth", true); + } + case TLS -> { + properties.put("mail.store.protocol", "imaps"); + properties.put("mail.imaps.host", imap.imapHost()); + properties.put("mail.imaps.port", imap.imapPort().toString()); + properties.put("mail.imaps.auth", true); + properties.put("mail.imaps.starttls.enable", true); + properties.put("mail.imaps.usesocketchannels", true); + } + case SSL -> { + properties.put("mail.store.protocol", "imaps"); + properties.put("mail.imaps.host", imap.imapHost()); + properties.put("mail.imaps.port", imap.imapPort().toString()); + properties.put("mail.imaps.auth", true); + properties.put("mail.imaps.ssl.enable", true); + properties.put("mail.imaps.usesocketchannel", true); + } + } + return properties; + } + + public Comparator retrieveEmailComparator( + @NotNull SortFieldPop3 sortFieldPop3, @NotNull SortOrder sortOrder) { + return (email1, email2) -> + switch (sortFieldPop3) { + case SENT_DATE -> sortOrder.order(email1.sentAt().compareTo(email2.sentAt())); + case SIZE -> sortOrder.order(email1.size().compareTo(email2.size())); + }; + } + + public Comparator retrieveEmailComparator( + @NotNull SortFieldImap sortFieldImap, @NotNull SortOrder sortOrder) { + return (email1, email2) -> + switch (sortFieldImap) { + case RECEIVED_DATE -> sortOrder.order(email1.receivedAt().compareTo(email2.receivedAt())); + case SENT_DATE -> sortOrder.order(email1.sentAt().compareTo(email2.sentAt())); + case SIZE -> sortOrder.order(email1.size().compareTo(email2.size())); + }; + } + + private Folder findFolderRecursively(Folder rootFolder, String targetFolder) + throws MessagingException { + if (targetFolder == null || targetFolder.isEmpty() || "INBOX".equals(targetFolder)) { + return rootFolder.getFolder("INBOX"); + } + Folder[] folders = rootFolder.list(); + for (Folder folder : folders) { + if (folder.getName().equals(targetFolder)) { + return folder; + } else { + Folder folderReturned = findFolderRecursively(folder, targetFolder); + if (folderReturned != null) { + return folderReturned; + } + } + } + return null; + } + + public Folder findImapFolder(Folder rootFolder, String targetFolder) throws MessagingException { + Folder folder = findFolderRecursively(rootFolder, targetFolder); + if (folder != null) { + return folder; + } + throw new MessagingException("Unable to find IMAP folder"); + } + + public Email createBodylessEmail(Message message) { + try { + List from = + Arrays.stream(Optional.ofNullable(message.getFrom()).orElse(new Address[0])) + .map(Address::toString) + .map(address -> address.replaceAll(".*<|>.*", "")) + .toList(); + List to = + Arrays.stream( + Optional.ofNullable(message.getRecipients(Message.RecipientType.TO)) + .orElse(new Address[0])) + .map(Address::toString) + .map(address -> address.replaceAll(".*<|>.*", "")) + .toList(); + List cc = + Arrays.stream( + Optional.ofNullable(message.getRecipients(Message.RecipientType.CC)) + .orElse(new Address[0])) + .map(Address::toString) + .map(address -> address.replaceAll(".*<|>.*", "")) + .toList(); + OffsetDateTime sentAt = + Optional.ofNullable(message.getSentDate()) + .map(Date::toInstant) + .map(instant -> instant.atOffset(ZoneOffset.UTC)) + .orElse(null); + OffsetDateTime receivedAt = + Optional.ofNullable(message.getReceivedDate()) + .map(Date::toInstant) + .map(instant -> instant.atOffset(ZoneOffset.UTC)) + .orElse(null); + String messageId = stripMessageId(message.getHeader("Message-ID")[0]); + return new Email( + null, + messageId, + message.getSubject(), + from, + to, + cc, + sentAt, + receivedAt, + message.getSize()); + + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } + + public Email createEmail(Message message) { + try { + Object bodyObject = message.getContent(); + Email email = this.createBodylessEmail(message); + EmailBody emailBody = + switch (bodyObject) { + case MimeMultipart multipart -> processMultipart(multipart, EmailBody.createBuilder()); + case String bodyAsPlainText when message.isMimeType("text/plain") -> + EmailBody.createBuilder().withBodyAsPlainText(bodyAsPlainText).build(); + case String bodyAsHtml when message.isMimeType("text/html") -> + EmailBody.createBuilder().withBodyAsHtml(bodyAsHtml).build(); + default -> throw new IllegalStateException("Unexpected part: " + bodyObject); + }; + return new Email( + emailBody, + email.messageId(), + email.subject(), + email.from(), + email.to(), + email.cc(), + email.sentAt(), + email.receivedAt(), + email.size()); + } catch (IOException | MessagingException e) { + throw new RuntimeException(e); + } + } + + private EmailBody processMultipart( + MimeMultipart multipart, EmailBody.EmailBodyBuilder emailBodyBuilder) { + try { + for (int i = 0; i < multipart.getCount(); i++) { + processBodyPart(multipart, emailBodyBuilder, i); + } + return emailBodyBuilder.build(); + } catch (MessagingException | IOException e) { + throw new RuntimeException(e); + } + } + + private void processBodyPart( + MimeMultipart multipart, EmailBody.EmailBodyBuilder emailBodyBuilder, int i) + throws MessagingException, IOException { + BodyPart bodyPart = multipart.getBodyPart(i); + switch (bodyPart.getContent()) { + case InputStream attachment when bodyPart + .getDisposition() + .equalsIgnoreCase(Part.ATTACHMENT) -> + emailBodyBuilder.addAttachment( + new EmailAttachment(attachment, bodyPart.getFileName(), bodyPart.getContentType())); + case String plainText when bodyPart.isMimeType("text/plain") -> + emailBodyBuilder.withBodyAsPlainText(plainText); + case String html when bodyPart.isMimeType("text/html") -> + emailBodyBuilder.withBodyAsHtml(html); + case MimeMultipart nestedMultipart -> processMultipart(nestedMultipart, emailBodyBuilder); + default -> + LOGGER.warn( + "This part is not yet managed. Mime : {}, disposition: {}", + bodyPart.getContentType(), + bodyPart.getDisposition()); + } + } + + private String stripMessageId(String messageId) { + if (messageId == null) return null; + return messageId.trim().replaceAll("[<>]", ""); + } + + public void moveMessage(Store store, Message message, String targetFolder) { + try { + Folder imapFolder = message.getFolder(); + Folder targetImapFolder = + store.getFolder( + String.join(String.valueOf(imapFolder.getSeparator()), targetFolder.split("\\."))); + if (!targetImapFolder.exists()) targetImapFolder.create(Folder.HOLDS_MESSAGES); + targetImapFolder.open(Folder.READ_WRITE); + imapFolder.copyMessages(new Message[] {message}, targetImapFolder); + this.markAsDeleted(message); + targetImapFolder.close(); + } catch (MessagingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/config/Configuration.java b/connectors/email/src/main/java/io/camunda/connector/email/config/Configuration.java new file mode 100644 index 0000000000..29e30df404 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/config/Configuration.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.config; + +public sealed interface Configuration permits ImapConfig, Pop3Config, SmtpConfig {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/config/CryptographicProtocol.java b/connectors/email/src/main/java/io/camunda/connector/email/config/CryptographicProtocol.java new file mode 100644 index 0000000000..fa016571eb --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/config/CryptographicProtocol.java @@ -0,0 +1,13 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.config; + +public enum CryptographicProtocol { + NONE, + TLS, + SSL; +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/config/ImapConfig.java b/connectors/email/src/main/java/io/camunda/connector/email/config/ImapConfig.java new file mode 100644 index 0000000000..427e3191af --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/config/ImapConfig.java @@ -0,0 +1,56 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.config; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +public record ImapConfig( + @TemplateProperty( + label = "IMAP Host", + group = "protocol", + id = "data.imapHost", + tooltip = + "Enter the address of the IMAP server used to retrieve your emails. This server allows you to sync your messages across multiple devices. (e.g., imap.example.com)", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapConfig.imapHost")) + @Valid + @NotNull + String imapHost, + @TemplateProperty( + label = "IMAP Port", + group = "protocol", + id = "data.imapPort", + tooltip = + "Enter the port number for connecting to the IMAP server. Common ports are 993 for secure connections using SSL/TLS, or 143 for non-secure connections.", + defaultValue = "993", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapConfig.imapPort")) + @Valid + @NotNull + Integer imapPort, + @TemplateProperty( + label = "Encryption protocol", + tooltip = "Select the encryption protocol for email security.", + group = "protocol", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "TLS", + choices = { + @TemplateProperty.DropdownPropertyChoice(label = "TLS", value = "TLS"), + @TemplateProperty.DropdownPropertyChoice(label = "None", value = "NONE"), + @TemplateProperty.DropdownPropertyChoice(label = "SSL", value = "SSL") + }, + binding = + @TemplateProperty.PropertyBinding( + name = "data.imapConfig.imapCryptographicProtocol")) + @NotNull + CryptographicProtocol imapCryptographicProtocol) + implements Configuration {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/config/Pop3Config.java b/connectors/email/src/main/java/io/camunda/connector/email/config/Pop3Config.java new file mode 100644 index 0000000000..0e511eb1ae --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/config/Pop3Config.java @@ -0,0 +1,56 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.config; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +public record Pop3Config( + @TemplateProperty( + label = "POP3 Host", + group = "protocol", + id = "data.pop3Host", + tooltip = + "Enter the address of the POP3 server if you want to download your emails to a single device. This server is typically used for retrieving emails without syncing. (e.g., pop.example.com)", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Config.pop3Host")) + @Valid + @NotNull + String pop3Host, + @TemplateProperty( + label = "POP3 Port", + group = "protocol", + id = "data.pop3Port", + tooltip = + "Enter the port number for connecting to the POP3 server. The standard port is 995 for secure connections with SSL/TLS, or 110 for non-secure connections.", + defaultValue = "995", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Config.pop3Port")) + @Valid + @NotNull + Integer pop3Port, + @TemplateProperty( + label = "Cryptographic protocol", + tooltip = "Select the encryption protocol for email security.", + group = "protocol", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "TLS", + choices = { + @TemplateProperty.DropdownPropertyChoice(label = "TLS", value = "TLS"), + @TemplateProperty.DropdownPropertyChoice(label = "None", value = "NONE"), + @TemplateProperty.DropdownPropertyChoice(label = "SSL", value = "SSL") + }, + binding = + @TemplateProperty.PropertyBinding( + name = "data.pop3Config.pop3CryptographicProtocol")) + @NotNull + CryptographicProtocol pop3CryptographicProtocol) + implements Configuration {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/config/SmtpConfig.java b/connectors/email/src/main/java/io/camunda/connector/email/config/SmtpConfig.java new file mode 100644 index 0000000000..4f7f521f71 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/config/SmtpConfig.java @@ -0,0 +1,56 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.config; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +public record SmtpConfig( + @TemplateProperty( + label = "SMTP Host", + group = "protocol", + id = "data.smtpHost", + tooltip = + "Provide the address of the SMTP server used for sending emails. This server handles the delivery of your outgoing messages. (e.g., smtp.example.com)", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.smtpConfig.smtpHost")) + @Valid + @NotNull + String smtpHost, + @TemplateProperty( + label = "SMTP Port", + group = "protocol", + id = "data.smtpPort", + tooltip = + "Enter the port number for connecting to the SMTP server. Typically, port 587 is used for secure connections with STARTTLS, port 465 for secure connections using SSL/TLS, and port 25 for non-secure connections.", + defaultValue = "587", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.smtpConfig.smtpPort")) + @Valid + @NotNull + Integer smtpPort, + @TemplateProperty( + label = "Cryptographic protocol", + tooltip = "Select the encryption protocol for email security.", + group = "protocol", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "TLS", + choices = { + @TemplateProperty.DropdownPropertyChoice(label = "TLS", value = "TLS"), + @TemplateProperty.DropdownPropertyChoice(label = "None", value = "NONE"), + @TemplateProperty.DropdownPropertyChoice(label = "SSL", value = "SSL") + }, + binding = + @TemplateProperty.PropertyBinding( + name = "data.smtpConfig.smtpCryptographicProtocol")) + @NotNull + CryptographicProtocol smtpCryptographicProtocol) + implements Configuration {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/inbound/EmailConnectorExecutable.java b/connectors/email/src/main/java/io/camunda/connector/email/inbound/EmailConnectorExecutable.java new file mode 100644 index 0000000000..4add5280e4 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/inbound/EmailConnectorExecutable.java @@ -0,0 +1,75 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.inbound; + +import io.camunda.connector.api.annotation.InboundConnector; +import io.camunda.connector.api.inbound.Health; +import io.camunda.connector.api.inbound.InboundConnectorContext; +import io.camunda.connector.api.inbound.InboundConnectorExecutable; +import io.camunda.connector.email.client.EmailListener; +import io.camunda.connector.email.client.jakarta.JakartaEmailListener; +import io.camunda.connector.email.client.jakarta.JakartaUtils; +import io.camunda.connector.email.inbound.model.EmailInboundConnectorProperties; +import io.camunda.connector.generator.dsl.BpmnType; +import io.camunda.connector.generator.java.annotation.ElementTemplate; + +@InboundConnector(name = "Email Consumer", type = "io.camunda:connector-email-inbound:1") +@ElementTemplate( + id = "io.camunda.connectors.email", + name = "Email Event Connector", + icon = "icon.svg", + version = 1, + inputDataClass = EmailInboundConnectorProperties.class, + description = "Consume emails", + documentationRef = + "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email", + propertyGroups = { + @ElementTemplate.PropertyGroup(id = "authentication", label = "Authentication"), + @ElementTemplate.PropertyGroup(id = "protocol", label = "Imap Details"), + @ElementTemplate.PropertyGroup(id = "listenerInfos", label = "Listener information"), + }, + elementTypes = { + @ElementTemplate.ConnectorElementType( + appliesTo = BpmnType.START_EVENT, + elementType = BpmnType.MESSAGE_START_EVENT, + templateIdOverride = "io.camunda.connectors.inbound.EmailMessageStart.v1", + templateNameOverride = "Email Message Start Event Connector"), + @ElementTemplate.ConnectorElementType( + appliesTo = {BpmnType.INTERMEDIATE_THROW_EVENT, BpmnType.INTERMEDIATE_CATCH_EVENT}, + elementType = BpmnType.INTERMEDIATE_CATCH_EVENT, + templateIdOverride = "io.camunda.connectors.inbound.EmailIntermediate.v1", + templateNameOverride = "Email Intermediate Catch Event Connector"), + @ElementTemplate.ConnectorElementType( + appliesTo = BpmnType.BOUNDARY_EVENT, + elementType = BpmnType.BOUNDARY_EVENT, + templateIdOverride = "io.camunda.connectors.inbound.EmailBoundary.v1", + templateNameOverride = "Email Boundary Event Connector") + }) +public class EmailConnectorExecutable + implements InboundConnectorExecutable { + + private final EmailListener emailListener; + + private EmailConnectorExecutable(JakartaEmailListener jakartaEmailListener) { + this.emailListener = jakartaEmailListener; + } + + public EmailConnectorExecutable() { + this(JakartaEmailListener.create(new JakartaUtils())); + } + + @Override + public void activate(InboundConnectorContext context) { + this.emailListener.startListener(context); + context.reportHealth(Health.up()); + } + + @Override + public void deactivate() { + this.emailListener.stopListener(); + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailInboundConnectorProperties.java b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailInboundConnectorProperties.java new file mode 100644 index 0000000000..d750a27c9f --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailInboundConnectorProperties.java @@ -0,0 +1,18 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.inbound.model; + +import io.camunda.connector.email.authentication.Authentication; +import io.camunda.connector.generator.java.annotation.NestedProperties; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +public record EmailInboundConnectorProperties( + @TemplateProperty(group = "authentication", id = "type") @Valid @NotNull + Authentication authentication, + @NestedProperties(addNestedPath = false) @Valid EmailListenerConfig data) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailListenerConfig.java b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailListenerConfig.java new file mode 100644 index 0000000000..194f9e4301 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/EmailListenerConfig.java @@ -0,0 +1,87 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.inbound.model; + +import io.camunda.connector.email.config.ImapConfig; +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.NestedProperties; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +public record EmailListenerConfig( + @NestedProperties(addNestedPath = false) @Valid ImapConfig imapConfig, + @TemplateProperty( + label = "Folder to listen", + group = "listenerInfos", + id = "data.folderToListen", + tooltip = + "Enter the names of the folders you wish to monitor, separated by commas, for new emails or changes (e.g., 'INBOX, Sent, Archive'). If left blank, the listener will default to monitoring the 'INBOX' folder.", + optional = true, + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.folderToListen")) + Object folderToListen, + @TemplateProperty( + label = "Sync strategy", + tooltip = "Chose the desired polling strategy", + group = "listenerInfos", + id = "data.initialPollingConfig", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "UNSEEN", + choices = { + @TemplateProperty.DropdownPropertyChoice( + label = "Unseen emails will be sync", + value = "UNSEEN"), + @TemplateProperty.DropdownPropertyChoice( + label = "No initial sync. Only new emails", + value = "NONE"), + @TemplateProperty.DropdownPropertyChoice( + label = "All emails will be sync", + value = "ALL") + }, + binding = @TemplateProperty.PropertyBinding(name = "data.initialPollingConfig")) + @NotNull + InitialPollingConfig initialPollingConfig, + @TemplateProperty( + label = "Handling strategy", + tooltip = "Chose the desired handling strategy", + group = "listenerInfos", + id = "data.handlingStrategy", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "READ", + choices = { + @TemplateProperty.DropdownPropertyChoice( + label = "Mark as read after processing", + value = "READ"), + @TemplateProperty.DropdownPropertyChoice(label = "Do nothing", value = "NO_HANDLING"), + @TemplateProperty.DropdownPropertyChoice( + label = "Delete after processing", + value = "DELETE"), + @TemplateProperty.DropdownPropertyChoice( + label = "Move to another folder after processing", + value = "MOVE") + }, + binding = @TemplateProperty.PropertyBinding(name = "data.handlingStrategy")) + @NotNull + HandlingStrategy handlingStrategy, + @TemplateProperty( + label = "Choose the target folder", + tooltip = + "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + group = "listenerInfos", + id = "data.targetFolder", + binding = @TemplateProperty.PropertyBinding(name = "data.targetFolder"), + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + condition = + @TemplateProperty.PropertyCondition( + property = "data.handlingStrategy", + equals = "MOVE")) + String targetFolder) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/HandlingStrategy.java b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/HandlingStrategy.java new file mode 100644 index 0000000000..bd22e2d6e2 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/HandlingStrategy.java @@ -0,0 +1,14 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.inbound.model; + +public enum HandlingStrategy { + READ, + DELETE, + NO_HANDLING, + MOVE; +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/InitialPollingConfig.java b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/InitialPollingConfig.java new file mode 100644 index 0000000000..ba99eecfb0 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/inbound/model/InitialPollingConfig.java @@ -0,0 +1,13 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.inbound.model; + +public enum InitialPollingConfig { + UNSEEN, + ALL, + NONE; +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/EmailConnectorFunction.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/EmailConnectorFunction.java new file mode 100644 index 0000000000..801b1ff2c7 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/EmailConnectorFunction.java @@ -0,0 +1,68 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound; + +import io.camunda.connector.api.annotation.OutboundConnector; +import io.camunda.connector.api.json.ConnectorsObjectMapperSupplier; +import io.camunda.connector.api.outbound.OutboundConnectorContext; +import io.camunda.connector.api.outbound.OutboundConnectorFunction; +import io.camunda.connector.email.client.EmailActionExecutor; +import io.camunda.connector.email.client.jakarta.JakartaEmailActionExecutor; +import io.camunda.connector.email.client.jakarta.JakartaUtils; +import io.camunda.connector.email.outbound.model.EmailRequest; +import io.camunda.connector.generator.java.annotation.ElementTemplate; + +@OutboundConnector( + name = "Email", + inputVariables = {"authentication", "protocol", "data"}, + type = "io.camunda:email:1") +@ElementTemplate( + id = "io.camunda.connectors.email.v1", + name = "Email Connector", + description = "Execute email requests", + inputDataClass = EmailRequest.class, + version = 1, + propertyGroups = { + @ElementTemplate.PropertyGroup(id = "authentication", label = "Authentication"), + @ElementTemplate.PropertyGroup(id = "protocol", label = "Protocol"), + @ElementTemplate.PropertyGroup(id = "smtpAction", label = "SMTP Action"), + @ElementTemplate.PropertyGroup(id = "pop3Action", label = "POP3 Action"), + @ElementTemplate.PropertyGroup(id = "imapAction", label = "IMAP Action"), + @ElementTemplate.PropertyGroup(id = "sendEmailSmtp", label = "Send Email"), + @ElementTemplate.PropertyGroup(id = "listEmailsPop3", label = "List Emails"), + @ElementTemplate.PropertyGroup(id = "searchEmailsPop3", label = "Search Emails"), + @ElementTemplate.PropertyGroup(id = "deleteEmailPop3", label = "Delete Email"), + @ElementTemplate.PropertyGroup(id = "readEmailPop3", label = "Read Email"), + @ElementTemplate.PropertyGroup(id = "listEmailsImap", label = "List Email"), + @ElementTemplate.PropertyGroup(id = "searchEmailsImap", label = "Search Emails"), + @ElementTemplate.PropertyGroup(id = "readEmailImap", label = "Read Email"), + @ElementTemplate.PropertyGroup(id = "deleteEmailImap", label = "Read Email"), + @ElementTemplate.PropertyGroup(id = "moveEmailImap", label = "Move Emails") + }, + documentationRef = + "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/", + icon = "icon.svg") +public class EmailConnectorFunction implements OutboundConnectorFunction { + + private final EmailActionExecutor emailActionExecutor; + + public EmailConnectorFunction() { + this( + JakartaEmailActionExecutor.create( + new JakartaUtils(), ConnectorsObjectMapperSupplier.getCopy())); + } + + public EmailConnectorFunction(EmailActionExecutor emailActionExecutor) { + this.emailActionExecutor = emailActionExecutor; + } + + @Override + public Object execute(OutboundConnectorContext context) { + EmailRequest emailRequest = context.bindVariables(EmailRequest.class); + return emailActionExecutor.execute(emailRequest); + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/model/EmailRequest.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/model/EmailRequest.java new file mode 100644 index 0000000000..df1e264e64 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/model/EmailRequest.java @@ -0,0 +1,37 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.model; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.camunda.connector.email.authentication.Authentication; +import io.camunda.connector.email.outbound.protocols.Imap; +import io.camunda.connector.email.outbound.protocols.Pop3; +import io.camunda.connector.email.outbound.protocols.Protocol; +import io.camunda.connector.email.outbound.protocols.Smtp; +import io.camunda.connector.generator.java.annotation.NestedProperties; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +public record EmailRequest( + @TemplateProperty(group = "authentication", id = "type") @Valid @NotNull + Authentication authentication, + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = "protocol") + @JsonSubTypes( + value = { + @JsonSubTypes.Type(value = Imap.class, name = "imap"), + @JsonSubTypes.Type(value = Pop3.class, name = "pop3"), + @JsonSubTypes.Type(value = Smtp.class, name = "smtp"), + }) + @Valid + @NotNull + @NestedProperties(addNestedPath = false) + Protocol data) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Imap.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Imap.java new file mode 100644 index 0000000000..ecdecda6be --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Imap.java @@ -0,0 +1,48 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.camunda.connector.email.config.Configuration; +import io.camunda.connector.email.config.ImapConfig; +import io.camunda.connector.email.outbound.protocols.actions.*; +import io.camunda.connector.generator.java.annotation.NestedProperties; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "imap", label = "IMAP") +public record Imap( + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = "imapActionDiscriminator") + @JsonSubTypes( + value = { + @JsonSubTypes.Type(value = ImapListEmails.class, name = "listEmailsImap"), + @JsonSubTypes.Type(value = ImapSearchEmails.class, name = "searchEmailsImap"), + @JsonSubTypes.Type(value = ImapMoveEmail.class, name = "moveEmailImap"), + @JsonSubTypes.Type(value = ImapReadEmail.class, name = "readEmailImap"), + @JsonSubTypes.Type(value = ImapDeleteEmail.class, name = "deleteEmailImap") + }) + @NestedProperties(addNestedPath = false) + @Valid + @NotNull + ImapAction imapAction, + @NestedProperties(addNestedPath = false) @Valid ImapConfig imapConfig) + implements Protocol { + @Override + public Action getProtocolAction() { + return imapAction; + } + + @Override + public Configuration getConfiguration() { + return imapConfig; + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Pop3.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Pop3.java new file mode 100644 index 0000000000..1f991e6bcb --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Pop3.java @@ -0,0 +1,47 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.camunda.connector.email.config.Configuration; +import io.camunda.connector.email.config.Pop3Config; +import io.camunda.connector.email.outbound.protocols.actions.*; +import io.camunda.connector.generator.java.annotation.NestedProperties; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "pop3", label = "POP3") +public record Pop3( + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = "pop3ActionDiscriminator") + @JsonSubTypes( + value = { + @JsonSubTypes.Type(value = Pop3ListEmails.class, name = "listEmailsPop3"), + @JsonSubTypes.Type(value = Pop3ReadEmail.class, name = "readEmailPop3"), + @JsonSubTypes.Type(value = Pop3DeleteEmail.class, name = "deleteEmailPop3"), + @JsonSubTypes.Type(value = Pop3SearchEmails.class, name = "searchEmailsPop3") + }) + @NestedProperties(addNestedPath = false) + @Valid + @NotNull + Pop3Action pop3Action, + @NestedProperties(addNestedPath = false) @Valid Pop3Config pop3Config) + implements Protocol { + @Override + public Action getProtocolAction() { + return pop3Action; + } + + @Override + public Configuration getConfiguration() { + return pop3Config; + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Protocol.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Protocol.java new file mode 100644 index 0000000000..323617f7aa --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Protocol.java @@ -0,0 +1,24 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols; + +import io.camunda.connector.email.config.Configuration; +import io.camunda.connector.email.outbound.protocols.actions.Action; +import io.camunda.connector.generator.java.annotation.TemplateDiscriminatorProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; + +@TemplateDiscriminatorProperty( + label = "Email Protocol", + group = "protocol", + name = "protocol", + defaultValue = "smtp") +@TemplateSubType(id = "protocol", label = "Email Protocol") +public sealed interface Protocol permits Imap, Pop3, Smtp { + Action getProtocolAction(); + + Configuration getConfiguration(); +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Smtp.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Smtp.java new file mode 100644 index 0000000000..78ad83cb70 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/Smtp.java @@ -0,0 +1,50 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.camunda.connector.email.config.Configuration; +import io.camunda.connector.email.config.SmtpConfig; +import io.camunda.connector.email.outbound.protocols.actions.Action; +import io.camunda.connector.email.outbound.protocols.actions.SmtpAction; +import io.camunda.connector.email.outbound.protocols.actions.SmtpSendEmail; +import io.camunda.connector.generator.java.annotation.NestedProperties; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "smtp", label = "SMTP") +public record Smtp( + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = "smtpActionDiscriminator") + @JsonSubTypes( + value = {@JsonSubTypes.Type(value = SmtpSendEmail.class, name = "sendEmailSmtp")}) + @NestedProperties(addNestedPath = false) + @Valid + @NotNull + SmtpAction smtpAction, + @NestedProperties(addNestedPath = false) @Valid SmtpConfig smtpConfig) + implements Protocol { + + @Override + public @Valid @NotNull SmtpAction smtpAction() { + return smtpAction; + } + + @Override + public Action getProtocolAction() { + return smtpAction; + } + + @Override + public Configuration getConfiguration() { + return smtpConfig; + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Action.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Action.java new file mode 100644 index 0000000000..f289c05e8c --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Action.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +public sealed interface Action permits SmtpAction, Pop3Action, ImapAction {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapAction.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapAction.java new file mode 100644 index 0000000000..c29cc1197b --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapAction.java @@ -0,0 +1,19 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.java.annotation.TemplateDiscriminatorProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; + +@TemplateDiscriminatorProperty( + label = "IMAP action", + group = "imapAction", + name = "data.imapActionDiscriminator", + defaultValue = "listEmailsImap") +@TemplateSubType(id = "data.imapActionDiscriminator", label = "IMAP Action") +public sealed interface ImapAction extends Action + permits ImapDeleteEmail, ImapListEmails, ImapMoveEmail, ImapReadEmail, ImapSearchEmails {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapDeleteEmail.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapDeleteEmail.java new file mode 100644 index 0000000000..cb068fe629 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapDeleteEmail.java @@ -0,0 +1,37 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "deleteEmailImap", label = "Delete an email") +public record ImapDeleteEmail( + @TemplateProperty( + label = "Message ID", + group = "deleteEmailImap", + id = "imapMessageIdDelete", + tooltip = "The ID of the message, typically returned by a previous email task.", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.messageId")) + @Valid + @NotNull + String messageId, + @TemplateProperty( + label = "Folder", + group = "deleteEmailImap", + id = "deleteEmailFolder", + tooltip = + "Specify the name of the folder from which you want to delete emails. If left blank, the default 'INBOX' will be used. For example, you can enter 'Trash' to delete emails from the Trash folder.", + optional = true, + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.deleteEmailFolder")) + String deleteEmailFolder) + implements ImapAction {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapListEmails.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapListEmails.java new file mode 100644 index 0000000000..475d2f5af1 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapListEmails.java @@ -0,0 +1,76 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "listEmailsImap", label = "List emails") +public record ImapListEmails( + @TemplateProperty( + label = "Maximum number of emails to be read", + group = "listEmailsImap", + id = "imapMaxToBeRead", + defaultValue = "100", + tooltip = + "Enter the maximum number of emails to be read from the specified folder. This limits the number of emails fetched to avoid performance issues with large mailboxes. The default value is set to 100.", + feel = Property.FeelMode.disabled, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.maxToBeRead")) + @Valid + @NotNull + Integer maxToBeRead, + @TemplateProperty( + label = "Folder", + group = "listEmailsImap", + id = "imapListEmailsFolder", + tooltip = + "Specify the folder from which you want to list emails (e.g., 'INBOX', 'Sent', 'Drafts'). If left blank, emails will be listed from the default 'INBOX' folder.", + optional = true, + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.listEmailsFolder")) + String listEmailsFolder, + @TemplateProperty( + label = "Sort emails by", + tooltip = + "Choose the criterion by which the listed emails should be sorted. The default sorting is by 'Received Date'.", + id = "imapSortField", + group = "listEmailsImap", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "RECEIVED_DATE", + choices = { + @TemplateProperty.DropdownPropertyChoice( + label = "Received Date", + value = "RECEIVED_DATE"), + @TemplateProperty.DropdownPropertyChoice(label = "Sent Date", value = "SENT_DATE"), + @TemplateProperty.DropdownPropertyChoice(label = "Size", value = "SIZE") + }, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.sortField")) + @NotNull + SortFieldImap sortField, + @TemplateProperty( + label = "Sort order", + tooltip = + "Select the sort order for the emails. Choose 'ASC' for ascending order or 'DESC' for descending order. Ascending order will list older emails first, while descending order will list newer emails first. The default sort order is 'ASC'.", + group = "listEmailsImap", + id = "imapSortOrder", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "ASC", + choices = { + @TemplateProperty.DropdownPropertyChoice(label = "ASC", value = "ASC"), + @TemplateProperty.DropdownPropertyChoice(label = "DESC", value = "DESC") + }, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.sortOrder")) + @NotNull + SortOrder sortOrder) + implements ImapAction {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapMoveEmail.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapMoveEmail.java new file mode 100644 index 0000000000..4f4c9ded8f --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapMoveEmail.java @@ -0,0 +1,49 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "moveEmailImap", label = "Move email") +public record ImapMoveEmail( + @TemplateProperty( + label = "Message ID", + group = "moveEmailImap", + id = "imapMessageIdMove", + tooltip = "The ID of the message, typically returned by a previous email task.", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.messageId")) + @Valid + @NotNull + String messageId, + @TemplateProperty( + label = "Source folder", + group = "moveEmailImap", + id = "data.fromFolder", + tooltip = + "Enter the name of the folder from which the emails will be moved. This field is required. For example, enter 'INBOX' to move emails from your Inbox.", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.fromFolder")) + @Valid + @NotNull + String fromFolder, + @TemplateProperty( + label = "Target folder", + group = "moveEmailImap", + id = "data.toFolder", + tooltip = + "Specify the destination folder to which the emails will be moved. To create a new folder or a hierarchy of folders, use a dot-separated path (e.g., 'Archive' or 'Projects.2023.January'). If any part of the path does not exist, it will be created automatically.", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.toFolder")) + @Valid + @NotNull + String toFolder) + implements ImapAction {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapReadEmail.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapReadEmail.java new file mode 100644 index 0000000000..bd3fbe1c44 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapReadEmail.java @@ -0,0 +1,37 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "readEmailImap", label = "Read an email") +public record ImapReadEmail( + @TemplateProperty( + label = "Message ID", + group = "readEmailImap", + id = "imapMessageIdRead", + tooltip = "The ID of the message, typically returned by a previous email task.", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.messageId")) + @Valid + @NotNull + String messageId, + @TemplateProperty( + label = "Folder", + group = "readEmailImap", + id = "readEmailFolder", + tooltip = + "Enter the name of the folder from which you wish to read emails. If left blank, emails will be read from the default 'INBOX' folder.", + optional = true, + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.readEmailFolder")) + String readEmailFolder) + implements ImapAction {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapSearchEmails.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapSearchEmails.java new file mode 100644 index 0000000000..ab1055532d --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/ImapSearchEmails.java @@ -0,0 +1,36 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; + +@TemplateSubType(id = "searchEmailsImap", label = "Search emails") +public record ImapSearchEmails( + @TemplateProperty( + label = "Search criteria", + group = "searchEmailsImap", + id = "searchStringEmailImap", + tooltip = + "Define the search criteria using supported keywords and syntax to filter emails. Refer to our detailed documentation for full search syntax and examples: [Email Documentation](https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/).", + type = TemplateProperty.PropertyType.Text, + feel = Property.FeelMode.required, + optional = true, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.criteria")) + Object criteria, + @TemplateProperty( + label = "Folder", + group = "searchEmailsImap", + id = "searchEmailFolder", + tooltip = + "Specify the folder in which to conduct the email search. If left blank, the search will default to the 'INBOX' folder. You may also specify subfolders using a dot-separated path (e.g., 'INBOX.Archives').", + optional = true, + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.imapAction.searchEmailFolder")) + String searchEmailFolder) + implements ImapAction {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3Action.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3Action.java new file mode 100644 index 0000000000..1f61b699ae --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3Action.java @@ -0,0 +1,19 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.java.annotation.TemplateDiscriminatorProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; + +@TemplateDiscriminatorProperty( + label = "POP3 action", + group = "pop3Action", + name = "data.pop3ActionDiscriminator", + defaultValue = "listEmailsPop3") +@TemplateSubType(id = "data.pop3ActionDiscriminator", label = "POP3 Action") +public sealed interface Pop3Action extends Action + permits Pop3DeleteEmail, Pop3ListEmails, Pop3ReadEmail, Pop3SearchEmails {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3DeleteEmail.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3DeleteEmail.java new file mode 100644 index 0000000000..ad57205fb4 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3DeleteEmail.java @@ -0,0 +1,25 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "deleteEmailPop3", label = "Delete Email") +public record Pop3DeleteEmail( + @TemplateProperty( + label = "Message ID", + group = "deleteEmailPop3", + id = "pop3MessageIdDelete", + tooltip = "The ID of the message, typically returned by a previous email task.", + feel = Property.FeelMode.required, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Action.messageId")) + @NotNull + String messageId) + implements Pop3Action {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ListEmails.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ListEmails.java new file mode 100644 index 0000000000..5d559ffee6 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ListEmails.java @@ -0,0 +1,63 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "listEmailsPop3", label = "List Emails") +public record Pop3ListEmails( + @TemplateProperty( + label = "Maximum number of emails to be read", + group = "listEmailsPop3", + id = "pop3maxToBeRead", + defaultValue = "100", + tooltip = + "Enter the maximum number of emails to be read from the specified folder. This limits the number of emails fetched to avoid performance issues with large mailboxes. The default value is set to 100.", + feel = Property.FeelMode.disabled, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Action.maxToBeRead")) + @Valid + @NotNull + Integer maxToBeRead, + @TemplateProperty( + label = "Sort emails by", + tooltip = + "Choose the criterion by which the listed emails should be sorted. The default sorting is by 'Sent Date'.", + group = "listEmailsPop3", + id = "pop3SortField", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "SENT_DATE", + choices = { + @TemplateProperty.DropdownPropertyChoice(label = "Sent Date", value = "SENT_DATE"), + @TemplateProperty.DropdownPropertyChoice(label = "Size", value = "SIZE") + }, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Action.sortField")) + @NotNull + SortFieldPop3 sortField, + @TemplateProperty( + label = "Sort order", + tooltip = + "Select the sort order for the emails. Choose 'ASC' for ascending order or 'DESC' for descending order. Ascending order will list older emails first, while descending order will list newer emails first. The default sort order is 'ASC'.", + id = "pop3SortOrder", + group = "listEmailsPop3", + feel = Property.FeelMode.required, + type = TemplateProperty.PropertyType.Dropdown, + constraints = @TemplateProperty.PropertyConstraints(notEmpty = true), + defaultValue = "ASC", + choices = { + @TemplateProperty.DropdownPropertyChoice(label = "ASC", value = "ASC"), + @TemplateProperty.DropdownPropertyChoice(label = "DESC", value = "DESC") + }, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Action.sortOrder")) + @NotNull + SortOrder sortOrder) + implements Pop3Action {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ReadEmail.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ReadEmail.java new file mode 100644 index 0000000000..5dde5c343b --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3ReadEmail.java @@ -0,0 +1,37 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "readEmailPop3", label = "Read Email") +public record Pop3ReadEmail( + @TemplateProperty( + label = "Message ID", + group = "readEmailPop3", + id = "pop3MessageIdRead", + tooltip = "The ID of the message, typically returned by a previous email task.", + feel = Property.FeelMode.required, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Action.messageId")) + @Valid + @NotNull + String messageId, + @TemplateProperty( + label = "Delete after reading", + group = "readEmailPop3", + tooltip = + "Enable this option if you want the email to be automatically deleted from the server after it is read. By default, this option is turned off to retain emails on the server.", + type = TemplateProperty.PropertyType.Boolean, + defaultValue = "false", + defaultValueType = TemplateProperty.DefaultValueType.Boolean, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Action.deleteOnRead")) + boolean deleteOnRead) + implements Pop3Action {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3SearchEmails.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3SearchEmails.java new file mode 100644 index 0000000000..d434f30ef8 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/Pop3SearchEmails.java @@ -0,0 +1,26 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; + +@TemplateSubType(id = "searchEmailsPop3", label = "Search emails") +public record Pop3SearchEmails( + @TemplateProperty( + label = "Search criteria", + group = "searchEmailsPop3", + id = "searchStringEmailPop3", + tooltip = + "Define the search criteria using supported keywords and syntax to filter emails. Refer to our detailed documentation for full search syntax and examples: [Email Documentation](https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/email/).", + type = TemplateProperty.PropertyType.Text, + feel = Property.FeelMode.required, + optional = true, + binding = @TemplateProperty.PropertyBinding(name = "data.pop3Action.criteria")) + Object criteria) + implements Pop3Action {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchCriteria.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchCriteria.java new file mode 100644 index 0000000000..bf331f1a56 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchCriteria.java @@ -0,0 +1,13 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +public enum SearchCriteria { + FROM, + SUBJECT, + BODY; +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchOperator.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchOperator.java new file mode 100644 index 0000000000..3505297ed1 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SearchOperator.java @@ -0,0 +1,12 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +public enum SearchOperator { + AND, + OR; +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpAction.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpAction.java new file mode 100644 index 0000000000..bda1108f19 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpAction.java @@ -0,0 +1,18 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.java.annotation.TemplateDiscriminatorProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; + +@TemplateDiscriminatorProperty( + label = "SMTP action", + group = "smtpAction", + name = "data.smtpActionDiscriminator", + defaultValue = "sendEmailSmtp") +@TemplateSubType(id = "data.smtpActionDiscriminator", label = "SMTP Action") +public sealed interface SmtpAction extends Action permits SmtpSendEmail {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpSendEmail.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpSendEmail.java new file mode 100644 index 0000000000..15f95327c3 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SmtpSendEmail.java @@ -0,0 +1,83 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import io.camunda.connector.generator.dsl.Property; +import io.camunda.connector.generator.java.annotation.TemplateProperty; +import io.camunda.connector.generator.java.annotation.TemplateSubType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +@TemplateSubType(id = "sendEmailSmtp", label = "Send Email") +public record SmtpSendEmail( + @TemplateProperty( + label = "From", + group = "sendEmailSmtp", + id = "smtpFrom", + tooltip = + "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.smtpAction.from")) + @Valid + @NotNull + Object from, + @TemplateProperty( + label = "To", + group = "sendEmailSmtp", + id = "smtpTo", + tooltip = + "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.smtpAction.to")) + @Valid + @NotNull + Object to, + @TemplateProperty( + label = "Cc", + group = "sendEmailSmtp", + id = "smtpCc", + tooltip = + "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.smtpAction.cc"), + optional = true) + @Valid + Object cc, + @TemplateProperty( + label = "Bcc", + group = "sendEmailSmtp", + id = "smtpBcc", + tooltip = + "Comma-separated list of email, e.g., 'email1@domain.com,email2@domain.com' or '=[ \"email1@domain.com\", \"email2@domain.com\"]'", + feel = Property.FeelMode.optional, + binding = @TemplateProperty.PropertyBinding(name = "data.smtpAction.bcc"), + optional = true) + @Valid + Object bcc, + @TemplateProperty( + label = "Subject", + group = "sendEmailSmtp", + id = "smtpSubject", + type = TemplateProperty.PropertyType.String, + tooltip = "Email's subject", + binding = @TemplateProperty.PropertyBinding(name = "data.smtpAction.subject"), + feel = Property.FeelMode.optional) + @Valid + @NotNull + String subject, + @TemplateProperty( + label = "Email Content", + group = "sendEmailSmtp", + id = "smtpBody", + type = TemplateProperty.PropertyType.Text, + tooltip = "Email's content", + binding = @TemplateProperty.PropertyBinding(name = "data.smtpAction.body"), + feel = Property.FeelMode.optional) + @Valid + @NotNull + String body) + implements SmtpAction {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldImap.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldImap.java new file mode 100644 index 0000000000..0f90777914 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldImap.java @@ -0,0 +1,13 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +public enum SortFieldImap { + RECEIVED_DATE, + SENT_DATE, + SIZE; +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldPop3.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldPop3.java new file mode 100644 index 0000000000..d53b50ba40 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortFieldPop3.java @@ -0,0 +1,12 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +public enum SortFieldPop3 { + SENT_DATE, + SIZE; +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortOrder.java b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortOrder.java new file mode 100644 index 0000000000..b8f32d45cc --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/outbound/protocols/actions/SortOrder.java @@ -0,0 +1,24 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.outbound.protocols.actions; + +import java.util.function.Function; + +public enum SortOrder { + ASC(comparison -> comparison), + DESC(comparison -> -comparison); + + private final Function comparator; + + SortOrder(Function comparator) { + this.comparator = comparator; + } + + public Integer order(Integer value) { + return comparator.apply(value); + } +} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/response/DeleteEmailResponse.java b/connectors/email/src/main/java/io/camunda/connector/email/response/DeleteEmailResponse.java new file mode 100644 index 0000000000..0e2686e8c7 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/response/DeleteEmailResponse.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.response; + +public record DeleteEmailResponse(String messageId, boolean deleted) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/response/ListEmailsResponse.java b/connectors/email/src/main/java/io/camunda/connector/email/response/ListEmailsResponse.java new file mode 100644 index 0000000000..fcf141849b --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/response/ListEmailsResponse.java @@ -0,0 +1,12 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.response; + +import java.util.List; + +public record ListEmailsResponse( + String messageId, List fromAddresses, String subject, Integer size) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/response/MoveEmailResponse.java b/connectors/email/src/main/java/io/camunda/connector/email/response/MoveEmailResponse.java new file mode 100644 index 0000000000..5a4daf71a8 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/response/MoveEmailResponse.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.response; + +public record MoveEmailResponse(String messageId, String from, String to) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/response/ReadEmailResponse.java b/connectors/email/src/main/java/io/camunda/connector/email/response/ReadEmailResponse.java new file mode 100644 index 0000000000..7c5c5162e3 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/response/ReadEmailResponse.java @@ -0,0 +1,17 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.response; + +import java.util.List; + +public record ReadEmailResponse( + String messageId, + List fromAddresses, + String subject, + Integer size, + String plainTextBody, + String htmlBody) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/response/SearchEmailsResponse.java b/connectors/email/src/main/java/io/camunda/connector/email/response/SearchEmailsResponse.java new file mode 100644 index 0000000000..0f7b4dc41f --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/response/SearchEmailsResponse.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.response; + +public record SearchEmailsResponse(String messageId, String subject) {} diff --git a/connectors/email/src/main/java/io/camunda/connector/email/response/SendEmailResponse.java b/connectors/email/src/main/java/io/camunda/connector/email/response/SendEmailResponse.java new file mode 100644 index 0000000000..04b419e2a9 --- /dev/null +++ b/connectors/email/src/main/java/io/camunda/connector/email/response/SendEmailResponse.java @@ -0,0 +1,9 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.response; + +public record SendEmailResponse(String subject, boolean sent) {} diff --git a/connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.inbound.InboundConnectorExecutable b/connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.inbound.InboundConnectorExecutable new file mode 100644 index 0000000000..0f1ea0f1ff --- /dev/null +++ b/connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.inbound.InboundConnectorExecutable @@ -0,0 +1 @@ +io.camunda.connector.email.inbound.EmailConnectorExecutable \ No newline at end of file diff --git a/connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction b/connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction new file mode 100644 index 0000000000..e7b32611ab --- /dev/null +++ b/connectors/email/src/main/resources/META-INF/services/io.camunda.connector.api.outbound.OutboundConnectorFunction @@ -0,0 +1 @@ +io.camunda.connector.email.outbound.EmailConnectorFunction \ No newline at end of file diff --git a/connectors/email/src/main/resources/icon.svg b/connectors/email/src/main/resources/icon.svg new file mode 100644 index 0000000000..7491c06277 --- /dev/null +++ b/connectors/email/src/main/resources/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaExecutorTest.java b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaExecutorTest.java new file mode 100644 index 0000000000..8b0a83e69e --- /dev/null +++ b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaExecutorTest.java @@ -0,0 +1,634 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.camunda.connector.email.authentication.SimpleAuthentication; +import io.camunda.connector.email.outbound.model.EmailRequest; +import io.camunda.connector.email.outbound.protocols.Imap; +import io.camunda.connector.email.outbound.protocols.Pop3; +import io.camunda.connector.email.outbound.protocols.Protocol; +import io.camunda.connector.email.outbound.protocols.Smtp; +import io.camunda.connector.email.outbound.protocols.actions.*; +import io.camunda.connector.email.response.DeleteEmailResponse; +import io.camunda.connector.email.response.ListEmailsResponse; +import io.camunda.connector.email.response.ReadEmailResponse; +import io.camunda.connector.email.response.SearchEmailsResponse; +import jakarta.mail.*; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.OffsetDateTime; +import java.util.*; +import org.eclipse.angus.mail.pop3.POP3Folder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class JakartaExecutorTest { + + @Test + void executeSmtpSendEmail() throws MessagingException { + + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = mock(ObjectMapper.class); + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + SmtpSendEmail smtpSendEmail = mock(SmtpSendEmail.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Smtp.class); + Session session = mock(Session.class); + Transport transport = mock(Transport.class); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(transport).connect(any(), any()); + + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(session.getProperties()).thenReturn(new Properties()); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(smtpSendEmail); + when(sessionFactory.createSession(any())).thenReturn(session); + when(smtpSendEmail.to()).thenReturn(List.of("to")); + when(smtpSendEmail.cc()).thenReturn(List.of("cc")); + when(smtpSendEmail.bcc()).thenReturn(List.of("bcc")); + when(smtpSendEmail.from()).thenReturn("myself"); + when(smtpSendEmail.body()).thenReturn("body"); + when(session.getTransport()).thenReturn(transport); + + actionExecutor.execute(emailRequest); + + verify(transport, times(1)) + .sendMessage( + argThat( + argument -> { + try { + return Arrays.stream(argument.getFrom()) + .allMatch(address -> address.toString().contains("myself")) + && argument.getContent().toString().contains("body"); + } catch (MessagingException | IOException e) { + throw new RuntimeException(e); + } + }), + argThat( + argument -> + Arrays.toString(argument).contains("to") + && Arrays.toString(argument).contains("cc") + && Arrays.toString(argument).contains("bcc"))); + } + + @Test + void executePop3ListEmails() throws MessagingException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = mock(ObjectMapper.class); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + Pop3ListEmails pop3ListEmails = mock(Pop3ListEmails.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Pop3.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + POP3Folder pop3Folder = mock(POP3Folder.class); + Message message = mock(Message.class); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + when(store.getFolder(anyString())).thenReturn(pop3Folder); + + doNothing().when(pop3Folder).open(Folder.READ_ONLY); + + when(pop3ListEmails.maxToBeRead()).thenReturn(10); + when(pop3Folder.getMessages()).thenReturn(new Message[] {message}); + + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(sessionFactory.retrieveEmailComparator((SortFieldPop3) any(), any())) + .thenReturn((o1, o2) -> 1); + when(message.getHeader(any())).thenReturn(new String[] {"id"}); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(pop3ListEmails); + when(sessionFactory.createSession(any())).thenReturn(session); + when(sessionFactory.createBodylessEmail(any())) + .thenReturn( + new Email( + null, + "1", + "", + List.of(""), + List.of(""), + List.of(""), + OffsetDateTime.now(), + OffsetDateTime.now(), + 1)); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(List.class, object); + Assertions.assertInstanceOf(ListEmailsResponse.class, ((List) object).getFirst()); + } + + @Test + void executeImapListEmails() throws MessagingException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = mock(ObjectMapper.class); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + ImapListEmails imapListEmails = mock(ImapListEmails.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Imap.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + Folder folder = mock(Folder.class); + Message message = mock(Message.class); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + doNothing().when(folder).open(Folder.READ_ONLY); + + when(imapListEmails.maxToBeRead()).thenReturn(10); + when(folder.getMessages()).thenReturn(new Message[] {message}); + + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(sessionFactory.findImapFolder(any(), any())).thenReturn(folder); + when(sessionFactory.retrieveEmailComparator((SortFieldImap) any(), any())) + .thenReturn((o1, o2) -> 1); + when(message.getHeader(any())).thenReturn(new String[] {"id"}); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(imapListEmails); + when(sessionFactory.createSession(any())).thenReturn(session); + when(sessionFactory.createBodylessEmail(any())) + .thenReturn( + new Email( + null, + "1", + "", + List.of(""), + List.of(""), + List.of(""), + OffsetDateTime.now(), + OffsetDateTime.now(), + 1)); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(List.class, object); + Assertions.assertInstanceOf(ListEmailsResponse.class, ((List) object).getFirst()); + } + + @Test + void executePop3ReadEmail() throws MessagingException, IOException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = mock(ObjectMapper.class); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + Pop3ReadEmail pop3ReadEmail = mock(Pop3ReadEmail.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Pop3.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + POP3Folder pop3Folder = mock(POP3Folder.class); + Message message = mock(Message.class); + + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + when(store.getFolder(anyString())).thenReturn(pop3Folder); + when(pop3Folder.search(any())).thenReturn(new Message[] {message}); + when(message.getHeader(any())).thenReturn(new String[] {"10"}); + when(pop3ReadEmail.messageId()).thenReturn("10"); + when(message.getContent()).thenReturn("string"); + when(message.isMimeType("text/plain")).thenReturn(true); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(pop3ReadEmail); + when(sessionFactory.createEmail(any())) + .thenReturn( + new Email( + new EmailBody("", "", List.of()), + "1", + "", + List.of(""), + List.of(""), + List.of(""), + OffsetDateTime.now(), + OffsetDateTime.now(), + 1)); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(ReadEmailResponse.class, object); + } + + @Test + void executeImapReadEmail() throws MessagingException, IOException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = mock(ObjectMapper.class); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + ImapReadEmail imapReadEmail = mock(ImapReadEmail.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Imap.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + Folder folder = mock(Folder.class); + Message message = mock(Message.class); + + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + when(sessionFactory.findImapFolder(any(), any())).thenReturn(folder); + when(folder.search(any())).thenReturn(new Message[] {message}); + when(message.getHeader(any())).thenReturn(new String[] {"10"}); + when(imapReadEmail.messageId()).thenReturn("10"); + when(message.getContent()).thenReturn("string"); + when(message.isMimeType("text/plain")).thenReturn(true); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(imapReadEmail); + when(sessionFactory.createEmail(any())) + .thenReturn( + new Email( + EmailBody.createBuilder().build(), + "1", + "", + List.of(""), + List.of(""), + List.of(""), + OffsetDateTime.now(), + OffsetDateTime.now(), + 1)); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(ReadEmailResponse.class, object); + } + + @Test + void executePop3DeleteEmail() throws MessagingException, IOException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = mock(ObjectMapper.class); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + Pop3DeleteEmail pop3DeleteEmail = mock(Pop3DeleteEmail.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Pop3.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + POP3Folder pop3Folder = mock(POP3Folder.class); + Message message = mock(Message.class); + + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + when(store.getFolder(anyString())).thenReturn(pop3Folder); + when(pop3Folder.search(any())).thenReturn(new Message[] {message}); + when(message.getHeader(any())).thenReturn(new String[] {"10"}); + when(pop3DeleteEmail.messageId()).thenReturn("10"); + when(message.getContent()).thenReturn("string"); + when(message.isMimeType("text/plain")).thenReturn(true); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(pop3DeleteEmail); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(DeleteEmailResponse.class, object); + } + + @Test + void executeImapDeleteEmail() throws MessagingException, IOException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = mock(ObjectMapper.class); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + ImapDeleteEmail imapDeleteEmail = mock(ImapDeleteEmail.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Imap.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + Folder folder = mock(Folder.class); + Message message = mock(Message.class); + + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + when(sessionFactory.findImapFolder(any(), any())).thenReturn(folder); + when(folder.search(any())).thenReturn(new Message[] {message}); + when(message.getHeader(any())).thenReturn(new String[] {"10"}); + when(imapDeleteEmail.messageId()).thenReturn("10"); + when(message.getContent()).thenReturn("string"); + when(message.isMimeType("text/plain")).thenReturn(true); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(imapDeleteEmail); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(DeleteEmailResponse.class, object); + } + + @Test + void executePop3SearchEmails() throws MessagingException, IOException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = new ObjectMapper(); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + Pop3SearchEmails pop3SearchEmails = mock(Pop3SearchEmails.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Pop3.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + POP3Folder pop3Folder = mock(POP3Folder.class); + Message message = mock(Message.class); + + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + when(store.getFolder(anyString())).thenReturn(pop3Folder); + when(pop3Folder.search(any())).thenReturn(new Message[] {message}); + when(pop3SearchEmails.criteria()) + .thenReturn(loadCriteria("src/test/resources/criterias/simple-criteria.json")); + when(message.getContent()).thenReturn("string"); + when(message.isMimeType("text/plain")).thenReturn(true); + when(message.getHeader(any())).thenReturn(new String[] {"1"}); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(pop3SearchEmails); + when(sessionFactory.createBodylessEmail(any())) + .thenReturn( + new Email( + null, + "1", + "", + List.of(""), + List.of(""), + List.of(""), + OffsetDateTime.now(), + OffsetDateTime.now(), + 1)); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(List.class, object); + } + + @Test + void executeImapSearchEmails() throws MessagingException, IOException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = new ObjectMapper(); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + ImapSearchEmails imapSearchEmails = mock(ImapSearchEmails.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Imap.class); + Session session = mock(Session.class); + Store store = mock(Store.class); + Folder folder = mock(Folder.class); + Message message = mock(Message.class); + + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + doNothing().when(store).connect(any(), any()); + + when(sessionFactory.findImapFolder(any(), any())).thenReturn(folder); + when(folder.search(any())).thenReturn(new Message[] {message}); + when(imapSearchEmails.criteria()) + .thenReturn(loadCriteria("src/test/resources/criterias/simple-criteria.json")); + when(message.getContent()).thenReturn("string"); + when(message.isMimeType("text/plain")).thenReturn(true); + when(message.getHeader(any())).thenReturn(new String[] {"1"}); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(session.getProperties()).thenReturn(new Properties()); + when(session.getStore()).thenReturn(store); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(imapSearchEmails); + when(sessionFactory.createBodylessEmail(any())) + .thenReturn( + new Email( + null, + "1", + "", + List.of(""), + List.of(""), + List.of(""), + OffsetDateTime.now(), + OffsetDateTime.now(), + 1)); + doNothing().when(store).connect(any(), any()); + + Object object = actionExecutor.execute(emailRequest); + + Assertions.assertInstanceOf(List.class, object); + } + + @Test + void executeImapSearchEmailsCriteriaSpecification() throws MessagingException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = new ObjectMapper(); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + ImapSearchEmails imapSearchEmails = mock(ImapSearchEmails.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Imap.class); + Session session = mock(Session.class); + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + when(sessionFactory.createBodylessEmail(any())).thenCallRealMethod(); + + Message message = + TestMessage.builder() + .setMessageId("10") + .setSubject("important") + .setFrom(List.of("camundi@camunda.com")) + .createTestMessage(); + Message message2 = + TestMessage.builder() + .setMessageId("12") + .setSubject("test") + .setFrom(List.of("camundi@camunda.com")) + .createTestMessage(); + Message message3 = + TestMessage.builder() + .setMessageId("11") + .setSubject("urgent") + .setFrom(List.of("test@camundal.com")) + .createTestMessage(); + + Store store = new TestStore(Session.getInstance(new Properties()), new URLName("")); + Folder folder = new TestFolder(store, message, message2, message3); + + when(session.getStore()).thenReturn(store); + when(sessionFactory.findImapFolder(any(), any())).thenReturn(folder); + when(imapSearchEmails.criteria()) + .thenReturn(loadCriteria("src/test/resources/criterias/simple-criteria.json")); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(imapSearchEmails); + + List searchEmailsResponses = + (List) actionExecutor.execute(emailRequest); + + Assertions.assertEquals(1, searchEmailsResponses.size()); + Assertions.assertEquals("important", searchEmailsResponses.getFirst().subject()); + } + + @Test + void executeImapSearchEmailsBodyCriteriaSpecification() throws MessagingException, IOException { + JakartaUtils sessionFactory = mock(JakartaUtils.class); + ObjectMapper objectMapper = new ObjectMapper(); + + JakartaEmailActionExecutor actionExecutor = + JakartaEmailActionExecutor.create(sessionFactory, objectMapper); + + EmailRequest emailRequest = mock(EmailRequest.class); + ImapSearchEmails imapSearchEmails = mock(ImapSearchEmails.class); + SimpleAuthentication simpleAuthentication = mock(SimpleAuthentication.class); + Protocol protocol = mock(Imap.class); + Session session = mock(Session.class); + when(sessionFactory.createSession(any())).thenReturn(session); + + // Authentication + when(simpleAuthentication.username()).thenReturn("user"); + when(simpleAuthentication.password()).thenReturn("secret"); + + Message message = + TestMessage.builder() + .setMessageId("10") + .setSubject("important") + .setBody("crazy") + .setFrom(List.of("camundi@camunda.com")) + .createTestMessage(); + Message message2 = + TestMessage.builder() + .setMessageId("12") + .setBody("crazy") + .setSubject("test") + .setFrom(List.of("camundi@camunda.com")) + .createTestMessage(); + Message message3 = + TestMessage.builder() + .setMessageId("11") + .setBody("crazy") + .setSubject("urgent") + .setFrom(List.of("test@camundal.com")) + .createTestMessage(); + + Store store = new TestStore(Session.getInstance(new Properties()), new URLName("")); + Folder folder = new TestFolder(store, message, message2, message3); + + when(session.getStore()).thenReturn(store); + when(sessionFactory.findImapFolder(any(), any())).thenReturn(folder); + when(imapSearchEmails.criteria()) + .thenReturn(loadCriteria("src/test/resources/criterias/body-criteria.json")); + when(emailRequest.authentication()).thenReturn(simpleAuthentication); + when(emailRequest.data()).thenReturn(protocol); + when(protocol.getProtocolAction()).thenReturn(imapSearchEmails); + when(sessionFactory.createBodylessEmail(any())).thenCallRealMethod(); + + List searchEmailsResponses = + (List) actionExecutor.execute(emailRequest); + + System.out.println(searchEmailsResponses); + Assertions.assertEquals(2, searchEmailsResponses.size()); + Assertions.assertEquals("10", searchEmailsResponses.get(0).messageId()); + Assertions.assertEquals("important", searchEmailsResponses.get(0).subject()); + Assertions.assertEquals("11", searchEmailsResponses.get(1).messageId()); + Assertions.assertEquals("urgent", searchEmailsResponses.get(1).subject()); + } + + private Object loadCriteria(String path) { + try { + return new ObjectMapper().readValue(Files.readString(Path.of(path)), Object.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaUtilsTest.java b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaUtilsTest.java new file mode 100644 index 0000000000..9ad54672cc --- /dev/null +++ b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/JakartaUtilsTest.java @@ -0,0 +1,184 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; + +import io.camunda.connector.email.authentication.Authentication; +import io.camunda.connector.email.authentication.SimpleAuthentication; +import io.camunda.connector.email.config.CryptographicProtocol; +import io.camunda.connector.email.config.ImapConfig; +import io.camunda.connector.email.config.Pop3Config; +import io.camunda.connector.email.config.SmtpConfig; +import io.camunda.connector.email.outbound.protocols.Smtp; +import jakarta.mail.Session; +import org.junit.jupiter.api.Test; + +class JakartaUtilsTest { + + @Test + void testCreateSessionWithSmtpAndTLS() { + // Given + SmtpConfig smtpConfig = new SmtpConfig("smtp.example.com", 587, CryptographicProtocol.TLS); + Smtp smtp = new Smtp(null, smtpConfig); + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + + // When + Session session = factory.createSession(smtp.smtpConfig()); + + // Then + assertEquals("smtp", session.getProperties().get("mail.transport.protocol")); + assertEquals("smtp.example.com", session.getProperties().get("mail.smtp.host")); + assertEquals("587", session.getProperties().get("mail.smtp.port")); + assertEquals("true", session.getProperties().get("mail.smtp.auth").toString()); + assertEquals("true", session.getProperties().get("mail.smtp.starttls.enable").toString()); + } + + @Test + void testCreateSessionWithSmtpAndNoSecurity() { + // Given + SmtpConfig smtpConfig = new SmtpConfig("smtp.example.com", 25, CryptographicProtocol.NONE); + Smtp smtp = new Smtp(null, smtpConfig); + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + + // When + Session session = factory.createSession(smtp.smtpConfig()); + + // Then + assertEquals("smtp", session.getProperties().get("mail.transport.protocol")); + assertEquals("smtp.example.com", session.getProperties().get("mail.smtp.host")); + assertEquals("25", session.getProperties().get("mail.smtp.port")); + assertEquals("true", session.getProperties().get("mail.smtp.auth").toString()); + assertNull(session.getProperties().get("mail.smtp.starttls.enable")); + assertNull(session.getProperties().get("mail.smtp.ssl.enable")); + } + + @Test + void testCreateSessionWithSmtpAndSSL() { + // Given + SmtpConfig smtpConfig = new SmtpConfig("smtp.ssl-example.com", 465, CryptographicProtocol.SSL); + Smtp smtp = new Smtp(null, smtpConfig); + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + + // When + Session session = factory.createSession(smtp.smtpConfig()); + + // Then + assertEquals("smtp", session.getProperties().get("mail.transport.protocol")); + assertEquals("smtp.ssl-example.com", session.getProperties().get("mail.smtp.host")); + assertEquals("465", session.getProperties().get("mail.smtp.port")); + assertEquals("true", session.getProperties().get("mail.smtp.auth").toString()); + assertEquals("true", session.getProperties().get("mail.smtp.ssl.enable").toString()); + assertNull(session.getProperties().get("mail.smtp.starttls.enable")); + } + + @Test + void testCreatePropertiesWithPop3AndNoSecurity() { + // Given + Pop3Config pop3Config = new Pop3Config("pop3.example.com", 110, CryptographicProtocol.NONE); + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + // When + Session session = factory.createSession(pop3Config); + + // Then + assertEquals("pop3", session.getProperties().get("mail.store.protocol")); + assertEquals("pop3.example.com", session.getProperties().get("mail.pop3.host")); + assertEquals("110", session.getProperties().get("mail.pop3.port")); + assertEquals("true", session.getProperties().get("mail.pop3.auth").toString()); + assertNull(session.getProperties().get("mail.pop3s.starttls.enable")); + assertNull(session.getProperties().get("mail.pop3s.ssl.enable")); + } + + @Test + void testCreatePropertiesWithPop3AndTLS() { + // Given + Pop3Config pop3Config = new Pop3Config("pop3.example.com", 995, CryptographicProtocol.TLS); + + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + // When + Session session = factory.createSession(pop3Config); + + // Then + assertEquals("pop3s", session.getProperties().get("mail.store.protocol")); + assertEquals("pop3.example.com", session.getProperties().get("mail.pop3s.host")); + assertEquals("995", session.getProperties().get("mail.pop3s.port")); + assertEquals("true", session.getProperties().get("mail.pop3s.auth").toString()); + assertEquals("true", session.getProperties().get("mail.pop3s.starttls.enable").toString()); + } + + @Test + void testCreatePropertiesWithImapAndNoSecurity() { + // Given + ImapConfig imapConfig = new ImapConfig("imap.example.com", 143, CryptographicProtocol.NONE); + + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + + Session session = factory.createSession(imapConfig); + + // Then + assertEquals("imap", session.getProperties().get("mail.store.protocol")); + assertEquals("imap.example.com", session.getProperties().get("mail.imap.host")); + assertEquals("143", session.getProperties().get("mail.imap.port")); + assertEquals("true", session.getProperties().get("mail.imap.auth").toString()); + assertNull(session.getProperties().get("mail.imaps.starttls.enable")); + assertNull(session.getProperties().get("mail.imaps.ssl.enable")); + } + + @Test + void testCreatePropertiesWithImapAndTLS() { + // Given + ImapConfig imapConfig = new ImapConfig("imap.tls-example.com", 993, CryptographicProtocol.TLS); + // When + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + + Session session = factory.createSession(imapConfig); + + // Then + assertEquals("imaps", session.getProperties().get("mail.store.protocol")); + assertEquals("imap.tls-example.com", session.getProperties().get("mail.imaps.host")); + assertEquals("993", session.getProperties().get("mail.imaps.port")); + assertEquals("true", session.getProperties().get("mail.imaps.auth").toString()); + assertEquals("true", session.getProperties().get("mail.imaps.starttls.enable").toString()); + assertNull(session.getProperties().get("mail.imaps.ssl.enable")); + } + + @Test + void testCreatePropertiesWithImapAndSSL() { + // Given + ImapConfig imapConfig = new ImapConfig("imap.ssl-example.com", 993, CryptographicProtocol.SSL); + + // When + Authentication auth = mock(SimpleAuthentication.class); + + JakartaUtils factory = new JakartaUtils(); + + Session session = factory.createSession(imapConfig); + + // Then + assertEquals("imaps", session.getProperties().get("mail.store.protocol")); + assertEquals("imap.ssl-example.com", session.getProperties().get("mail.imaps.host")); + assertEquals("993", session.getProperties().get("mail.imaps.port")); + assertEquals("true", session.getProperties().get("mail.imaps.auth").toString()); + assertEquals("true", session.getProperties().get("mail.imaps.ssl.enable").toString()); + assertNull(session.getProperties().get("mail.imaps.starttls.enable")); + } +} diff --git a/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestFolder.java b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestFolder.java new file mode 100644 index 0000000000..274daa354a --- /dev/null +++ b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestFolder.java @@ -0,0 +1,122 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import jakarta.mail.*; + +public class TestFolder extends Folder { + + private Message[] messages; + + /** + * Constructor that takes a Store object. + * + * @param store the Store that holds this folder + */ + protected TestFolder(Store store) { + super(store); + } + + public TestFolder(Store store, Message... messages) { + super(store); + this.messages = messages; + } + + @Override + public String getName() { + return ""; + } + + @Override + public String getFullName() { + return ""; + } + + @Override + public Folder getParent() throws MessagingException { + return null; + } + + @Override + public boolean exists() throws MessagingException { + return false; + } + + @Override + public Folder[] list(String pattern) throws MessagingException { + return new Folder[0]; + } + + @Override + public char getSeparator() throws MessagingException { + return 0; + } + + @Override + public int getType() throws MessagingException { + return 0; + } + + @Override + public boolean create(int type) throws MessagingException { + return false; + } + + @Override + public boolean hasNewMessages() throws MessagingException { + return false; + } + + @Override + public Folder getFolder(String name) throws MessagingException { + return null; + } + + @Override + public boolean delete(boolean recurse) throws MessagingException { + return false; + } + + @Override + public boolean renameTo(Folder f) throws MessagingException { + return false; + } + + @Override + public void open(int mode) throws MessagingException {} + + @Override + public void close(boolean expunge) throws MessagingException {} + + @Override + public boolean isOpen() { + return true; + } + + @Override + public Flags getPermanentFlags() { + return null; + } + + @Override + public int getMessageCount() throws MessagingException { + return this.messages.length; + } + + @Override + public Message getMessage(int msgnum) throws MessagingException { + return this.messages[msgnum - 1]; + } + + @Override + public void appendMessages(Message[] msgs) throws MessagingException {} + + @Override + public Message[] expunge() throws MessagingException { + return new Message[0]; + } +} diff --git a/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestMessage.java b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestMessage.java new file mode 100644 index 0000000000..04280a9fce --- /dev/null +++ b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestMessage.java @@ -0,0 +1,285 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import jakarta.activation.DataHandler; +import jakarta.mail.*; +import jakarta.mail.internet.InternetAddress; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.Random; +import java.util.random.RandomGenerator; + +public class TestMessage extends Message { + + private final String messageId; + private final String subject; + private final List from; + private final List to; + private final List cc; + private final OffsetDateTime sentAt; + private final OffsetDateTime receivedAt; + private final Integer size; + private final String body; + + public TestMessage( + String messageId, + String subject, + List from, + List strings, + List cc, + OffsetDateTime sentAt, + OffsetDateTime receivedAt, + Integer size, + String body) { + this.messageId = messageId; + this.subject = subject; + this.from = from; + to = strings; + this.cc = cc; + this.sentAt = sentAt; + this.receivedAt = receivedAt; + this.size = size; + this.body = body; + } + + public static TestMessageBuilder builder() { + return new TestMessageBuilder(); + } + + @Override + public Address[] getFrom() throws MessagingException { + return InternetAddress.parse(String.join(",", from)); + } + + @Override + public void setFrom(Address address) throws MessagingException {} + + @Override + public void setFrom() throws MessagingException {} + + @Override + public void addFrom(Address[] addresses) throws MessagingException {} + + @Override + public Address[] getRecipients(RecipientType type) throws MessagingException { + return new Address[0]; + } + + @Override + public void setRecipients(RecipientType type, Address[] addresses) throws MessagingException {} + + @Override + public void addRecipients(RecipientType type, Address[] addresses) throws MessagingException {} + + @Override + public String getSubject() throws MessagingException { + return this.subject; + } + + @Override + public void setSubject(String subject) throws MessagingException {} + + @Override + public Date getSentDate() throws MessagingException { + return null; + } + + @Override + public void setSentDate(Date date) throws MessagingException {} + + @Override + public Date getReceivedDate() throws MessagingException { + return null; + } + + @Override + public Flags getFlags() throws MessagingException { + return null; + } + + @Override + public void setFlags(Flags flag, boolean set) throws MessagingException {} + + @Override + public Message reply(boolean replyToAll) throws MessagingException { + return null; + } + + @Override + public void saveChanges() throws MessagingException {} + + @Override + public int getSize() throws MessagingException { + return Random.from(RandomGenerator.getDefault()).nextInt(10000); + } + + @Override + public int getLineCount() throws MessagingException { + return 0; + } + + @Override + public String getContentType() throws MessagingException { + return "text/plain"; + } + + @Override + public boolean isMimeType(String mimeType) throws MessagingException { + return true; + } + + @Override + public String getDisposition() throws MessagingException { + return ""; + } + + @Override + public void setDisposition(String disposition) throws MessagingException {} + + @Override + public String getDescription() throws MessagingException { + return ""; + } + + @Override + public void setDescription(String description) throws MessagingException {} + + @Override + public String getFileName() throws MessagingException { + return ""; + } + + @Override + public void setFileName(String filename) throws MessagingException {} + + @Override + public InputStream getInputStream() throws IOException, MessagingException { + return null; + } + + @Override + public DataHandler getDataHandler() throws MessagingException { + return null; + } + + @Override + public void setDataHandler(DataHandler dh) throws MessagingException {} + + @Override + public Object getContent() throws IOException, MessagingException { + return this.body; + } + + @Override + public void setContent(Multipart mp) throws MessagingException {} + + @Override + public void setContent(Object obj, String type) throws MessagingException {} + + @Override + public void setText(String text) throws MessagingException {} + + @Override + public void writeTo(OutputStream os) throws IOException, MessagingException {} + + @Override + public String[] getHeader(String header_name) throws MessagingException { + return this.messageId.split("/"); + } + + @Override + public void setHeader(String header_name, String header_value) throws MessagingException {} + + @Override + public void addHeader(String header_name, String header_value) throws MessagingException {} + + @Override + public void removeHeader(String header_name) throws MessagingException {} + + @Override + public Enumeration
getAllHeaders() throws MessagingException { + return null; + } + + @Override + public Enumeration
getMatchingHeaders(String[] header_names) throws MessagingException { + return null; + } + + @Override + public Enumeration
getNonMatchingHeaders(String[] header_names) + throws MessagingException { + return null; + } + + public static class TestMessageBuilder { + private String messageId; + private String subject; + private List from; + private List strings; + private List cc; + private OffsetDateTime sentAt; + private OffsetDateTime receivedAt; + private Integer size; + private String body; + + public TestMessageBuilder setMessageId(String messageId) { + this.messageId = messageId; + return this; + } + + public TestMessageBuilder setSubject(String subject) { + this.subject = subject; + return this; + } + + public TestMessageBuilder setFrom(List from) { + this.from = from; + return this; + } + + public TestMessageBuilder setStrings(List strings) { + this.strings = strings; + return this; + } + + public TestMessageBuilder setCc(List cc) { + this.cc = cc; + return this; + } + + public TestMessageBuilder setSentAt(OffsetDateTime sentAt) { + this.sentAt = sentAt; + return this; + } + + public TestMessageBuilder setReceivedAt(OffsetDateTime receivedAt) { + this.receivedAt = receivedAt; + return this; + } + + public TestMessageBuilder setSize(Integer size) { + this.size = size; + return this; + } + + public TestMessageBuilder setBody(String body) { + this.body = body; + return this; + } + + public TestMessage createTestMessage() { + return new TestMessage(messageId, subject, from, strings, cc, sentAt, receivedAt, size, body); + } + } +} diff --git a/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestStore.java b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestStore.java new file mode 100644 index 0000000000..9d4cafd332 --- /dev/null +++ b/connectors/email/src/test/java/io/camunda/connector/email/client/jakarta/TestStore.java @@ -0,0 +1,36 @@ +/* + * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH + * under one or more contributor license agreements. Licensed under a proprietary license. + * See the License.txt file for more information. You may not use this file + * except in compliance with the proprietary license. + */ +package io.camunda.connector.email.client.jakarta; + +import jakarta.mail.*; + +public class TestStore extends Store { + /** + * Constructor. + * + * @param session Session object for this Store. + * @param urlname URLName object to be used for this Store + */ + protected TestStore(Session session, URLName urlname) { + super(session, urlname); + } + + @Override + public Folder getDefaultFolder() throws MessagingException { + return null; + } + + @Override + public Folder getFolder(String name) throws MessagingException { + return null; + } + + @Override + public Folder getFolder(URLName url) throws MessagingException { + return null; + } +} diff --git a/connectors/email/src/test/resources/criterias/body-criteria.json b/connectors/email/src/test/resources/criterias/body-criteria.json new file mode 100644 index 0000000000..ddee698fbb --- /dev/null +++ b/connectors/email/src/test/resources/criterias/body-criteria.json @@ -0,0 +1,22 @@ +{ + "operator":"AND", + "criteria":[ + { + "field":"BODY", + "value":"crazy" + }, + { + "operator":"OR", + "criteria":[ + { + "field":"SUBJECT", + "value":"urgent" + }, + { + "field":"SUBJECT", + "value":"important" + } + ] + } + ] +} \ No newline at end of file diff --git a/connectors/email/src/test/resources/criterias/simple-criteria.json b/connectors/email/src/test/resources/criterias/simple-criteria.json new file mode 100644 index 0000000000..c508df6572 --- /dev/null +++ b/connectors/email/src/test/resources/criterias/simple-criteria.json @@ -0,0 +1,22 @@ +{ + "operator":"AND", + "criteria":[ + { + "field":"FROM", + "value":"camundi@camunda.com" + }, + { + "operator":"OR", + "criteria":[ + { + "field":"SUBJECT", + "value":"urgent" + }, + { + "field":"SUBJECT", + "value":"important" + } + ] + } + ] +} \ No newline at end of file diff --git a/connectors/pom.xml b/connectors/pom.xml index 8ddd406d01..c8e9e32085 100644 --- a/connectors/pom.xml +++ b/connectors/pom.xml @@ -20,6 +20,7 @@ automation-anywhere aws + email google http jdbc