Skip to content

Commit

Permalink
feat(sendgrid): attachment support
Browse files Browse the repository at this point in the history
  • Loading branch information
DenovVasil committed Jan 31, 2025
1 parent aba5e76 commit a6d9880
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"keywords" : [ ]
},
"documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/sendgrid/",
"version" : 3,
"version" : 4,
"category" : {
"id" : "connectors",
"name" : "Connectors"
Expand Down Expand Up @@ -233,6 +233,18 @@
"type" : "simple"
},
"type" : "Text"
}, {
"id" : "attachments",
"label" : "attachments",
"description" : "List of <a href=\"https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/create-document-link/\">Camunda Documents</a>",
"optional" : true,
"feel" : "required",
"group" : "content",
"binding" : {
"name" : "attachments",
"type" : "zeebe:input"
},
"type" : "String"
}, {
"id" : "resultVariable",
"label" : "Result variable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"keywords" : [ ]
},
"documentationRef" : "https://docs.camunda.io/docs/components/connectors/out-of-the-box-connectors/sendgrid/",
"version" : 3,
"version" : 4,
"category" : {
"id" : "connectors",
"name" : "Connectors"
Expand Down Expand Up @@ -228,6 +228,18 @@
"type" : "simple"
},
"type" : "Text"
}, {
"id" : "attachments",
"label" : "attachments",
"description" : "List of <a href=\"https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/create-document-link/\">Camunda Documents</a>",
"optional" : true,
"feel" : "required",
"group" : "content",
"binding" : {
"name" : "attachments",
"type" : "zeebe:input"
},
"type" : "String"
}, {
"id" : "resultVariable",
"label" : "Result variable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,29 @@
import com.sendgrid.Response;
import com.sendgrid.SendGrid;
import com.sendgrid.helpers.mail.Mail;
import com.sendgrid.helpers.mail.objects.Attachments;
import com.sendgrid.helpers.mail.objects.Personalization;
import io.camunda.connector.api.annotation.OutboundConnector;
import io.camunda.connector.api.outbound.OutboundConnectorContext;
import io.camunda.connector.api.outbound.OutboundConnectorFunction;
import io.camunda.connector.generator.java.annotation.ElementTemplate;
import io.camunda.connector.sendgrid.model.SendGridRequest;
import io.camunda.document.Document;
import java.io.IOException;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@OutboundConnector(
name = "SendGrid",
inputVariables = {"apiKey", "from", "to", "template", "content"},
inputVariables = {"apiKey", "from", "to", "template", "content", "attachments"},
type = "io.camunda:sendgrid:1")
@ElementTemplate(
id = "io.camunda.connectors.SendGrid.v2",
name = "SendGrid Outbound Connector",
description = "Send an email via SendGrid",
inputDataClass = SendGridRequest.class,
version = 3,
version = 4,
propertyGroups = {
@ElementTemplate.PropertyGroup(id = "authentication", label = "Authentication"),
@ElementTemplate.PropertyGroup(id = "sender", label = "Sender"),
Expand All @@ -43,11 +46,9 @@
icon = "icon.svg")
public class SendGridFunction implements OutboundConnectorFunction {

private static final Logger LOGGER = LoggerFactory.getLogger(SendGridFunction.class);

protected static final ObjectMapper objectMapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

private static final Logger LOGGER = LoggerFactory.getLogger(SendGridFunction.class);
private final SendGridClientSupplier sendGridSupplier;

public SendGridFunction() {
Expand Down Expand Up @@ -84,6 +85,7 @@ private Mail createEmail(final SendGridRequest request) {
mail.setFrom(request.getInnerSenGridEmailFrom());
addContentIfPresent(mail, request);
addTemplateIfPresent(mail, request);
addAttachmentIfPresent(mail, request.getAttachments());

return mail;
}
Expand All @@ -98,6 +100,18 @@ private void addTemplateIfPresent(final Mail mail, final SendGridRequest request
}
}

private void addAttachmentIfPresent(final Mail mail, List<Document> documents) {
if (documents != null && !documents.isEmpty()) {
documents.forEach(
document -> {
Attachments attachments =
new Attachments.Builder(document.metadata().getFileName(), document.asInputStream())
.build();
mail.addAttachments(attachments);
});
}
}

private void addContentIfPresent(final Mail mail, final SendGridRequest request) {
if (request.hasContent()) {
final SendGridRequest.Content content = request.getContent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import io.camunda.connector.generator.java.annotation.TemplateProperty.DropdownPropertyChoice;
import io.camunda.connector.generator.java.annotation.TemplateProperty.PropertyBinding;
import io.camunda.connector.generator.java.annotation.TemplateProperty.PropertyCondition;
import io.camunda.document.Document;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import java.util.Objects;

Expand Down Expand Up @@ -99,6 +101,16 @@ public record Content(
@Valid
private Content content;

@TemplateProperty(
id = "attachments",
group = "content",
label = "attachments",
optional = true,
feel = Property.FeelMode.required,
description =
"List of <a href=\"https://docs.camunda.io/docs/next/apis-tools/camunda-api-rest/specifications/create-document-link/\">Camunda Documents</a>")
private List<Document> attachments;

@AssertTrue(message = "must not be empty")
private boolean isSenderName() {
return from != null && isNotBlank(from.name());
Expand Down Expand Up @@ -192,6 +204,14 @@ public void setMailType(MailType mailType) {
this.mailType = mailType;
}

public List<Document> getAttachments() {
return attachments;
}

public void setAttachments(List<Document> attachments) {
this.attachments = attachments;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
Expand All @@ -205,12 +225,13 @@ public boolean equals(final Object o) {
&& Objects.equals(from, that.from)
&& Objects.equals(to, that.to)
&& Objects.equals(template, that.template)
&& Objects.equals(content, that.content);
&& Objects.equals(content, that.content)
&& Objects.equals(attachments, that.attachments);
}

@Override
public int hashCode() {
return Objects.hash(apiKey, from, to, template, content);
return Objects.hash(apiKey, from, to, template, content, attachments);
}

@Override
Expand All @@ -225,6 +246,8 @@ public String toString() {
+ template
+ ", content="
+ content
+ ", attachments="
+ attachments
+ '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ protected interface ActualValue {
String RECEIVER_NAME = "Jane Doe";
String SENDER_EMAIL = "[email protected]";
String SENDER_NAME = "John Doe";
String ATTACHED_FILE_NAME = "google-my-business-logo-png-transparent.png";

interface Content {
String SUBJECT = "subject_test";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;

import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.sendgrid.Method;
import com.sendgrid.Request;
import com.sendgrid.Response;
import com.sendgrid.SendGrid;
import io.camunda.client.api.response.DocumentMetadata;
import io.camunda.connector.api.outbound.OutboundConnectorContext;
import io.camunda.connector.sendgrid.model.SendGridRequest;
import io.camunda.connector.test.outbound.OutboundConnectorContextBuilder;
import io.camunda.document.Document;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
import org.junit.jupiter.api.Assertions;
Expand All @@ -42,6 +43,8 @@ public class SendGridFunctionTest extends BaseTest {
private static final String PERSONALIZATION_JSON_NAME = "personalizations";
private static final String NAME_JSON_NAME = "name";
private static final String EMAIL_JSON_NAME = "email";
private static final String ATTACHMENTS_JSON_NAME = "attachments";
private static final String ATTACHMENTS_FILE_NAME_JSON_NAME = "filename";

private OutboundConnectorContext context;
private SendGridFunction function;
Expand Down Expand Up @@ -107,9 +110,15 @@ public void execute_shouldReturnNullIfResponseStatusCodeIs202(int statusCode) th
public void execute_shouldCreateRequestWithMailAndExpectedData(String input) throws Exception {
// Given
context = contextBuilder.variables(input).build();
ArgumentCaptor<Request> requestArgumentCaptor = ArgumentCaptor.forClass(Request.class);

var contextWithMocketDocument = mock(OutboundConnectorContext.class);

var requestWithMockedDocument = prepareRequestWithDocumentMock(context);

when(contextWithMocketDocument.bindVariables(any())).thenReturn(requestWithMockedDocument);

// When
function.execute(context);
function.execute(contextWithMocketDocument);
verify(sendGridMock).api(requestArgumentCaptor.capture());
// Then we have POST request with mail participants,
Request requestValue = requestArgumentCaptor.getValue();
Expand All @@ -122,27 +131,42 @@ public void execute_shouldCreateRequestWithMailAndExpectedData(String input) thr
requestJsonObject.withObject(
JsonPointer.valueOf("/" + PERSONALIZATION_JSON_NAME + "/0/" + TO_JSON_NAME + "/0"));

var array = (ArrayNode) requestJsonObject.get(ATTACHMENTS_JSON_NAME);
String attachmentName = array.get(0).get(ATTACHMENTS_FILE_NAME_JSON_NAME).textValue();

assertThat(from.get(NAME_JSON_NAME).textValue()).isEqualTo(ActualValue.SENDER_NAME);
assertThat(from.get(EMAIL_JSON_NAME).textValue()).isEqualTo(ActualValue.SENDER_EMAIL);
assertThat(to.get(NAME_JSON_NAME).textValue()).isEqualTo(ActualValue.RECEIVER_NAME);
assertThat(to.get(EMAIL_JSON_NAME).textValue()).isEqualTo(ActualValue.RECEIVER_EMAIL);
assertThat(attachmentName).isEqualTo(ActualValue.ATTACHED_FILE_NAME);
}

@ParameterizedTest(name = "Should send mail with template. Test case # {index}")
@MethodSource("successSendMailByTemplateRequestCases")
public void execute_shouldSendMailByTemplateIfTemplateExist(String input) throws Exception {
// Given
context = contextBuilder.variables(input).build();
ArgumentCaptor<Request> requestArgumentCaptor = ArgumentCaptor.forClass(Request.class);

var contextWithMocketDocument = mock(OutboundConnectorContext.class);

var requestWithMockedDocument = prepareRequestWithDocumentMock(context);

when(contextWithMocketDocument.bindVariables(any())).thenReturn(requestWithMockedDocument);

// When
function.execute(context);
function.execute(contextWithMocketDocument);
verify(sendGridMock).api(requestArgumentCaptor.capture());
// Then we have 'template_id' in sendGridRequest with expected ID and 'content' is not exist
Request requestValue = requestArgumentCaptor.getValue();

var requestJsonObject = objectMapper.readTree(requestValue.getBody());

var array = (ArrayNode) requestJsonObject.get(ATTACHMENTS_JSON_NAME);
String attachmentName = array.get(0).get(ATTACHMENTS_FILE_NAME_JSON_NAME).textValue();
assertThat(requestJsonObject.get(TEMPLATE_ID_JSON_NAME).textValue())
.isEqualTo(ActualValue.Template.ID);
assertThat(requestJsonObject.has(CONTENT_JSON_NAME)).isFalse();
assertThat(attachmentName).isEqualTo(ActualValue.ATTACHED_FILE_NAME);
}

@ParameterizedTest(name = "Should send mail with content. Test case # {index}")
Expand All @@ -166,4 +190,19 @@ public void execute_shouldSendMailIfContentExist(String input) throws Exception

assertThat(requestJsonObject.has(TEMPLATE_ID_JSON_NAME)).isFalse();
}

private SendGridRequest prepareRequestWithDocumentMock(OutboundConnectorContext context) {
var request = context.bindVariables(SendGridRequest.class);

var document = mock(Document.class);
var documentMetadata = mock(DocumentMetadata.class);
when(document.metadata()).thenReturn(documentMetadata);

String fileName = request.getAttachments().getFirst().metadata().getFileName();
when(documentMetadata.getFileName()).thenReturn(fileName);
when(document.asInputStream()).thenReturn(new ByteArrayInputStream(new byte[0]));

request.setAttachments(List.of(document));
return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@
"shipAddress": "{{secrets.TEMPLATE_DATA_SHIP_ADDRESS}}",
"shipZip": "{{secrets.TEMPLATE_DATA_SHIP_ZIP}}"
}
}
},
"attachments" : [ {
"camunda.document.type" : "camunda",
"storeId" : "in-memory",
"documentId" : "2ea3bfcc-7683-4cb6-b0ce-8052ca1d6da0",
"metadata" : {
"contentType" : "image/png",
"fileName" : "google-my-business-logo-png-transparent.png",
"size" : 66497,
"customProperties" : { }
}
}]
},
{
"apiKey": "send_grid_key",
Expand All @@ -35,7 +46,18 @@
"shipAddress": "Krossener Str. 24",
"shipZip": "10245"
}
}
},
"attachments" : [ {
"camunda.document.type" : "camunda",
"storeId" : "in-memory",
"documentId" : "2ea3bfcc-7683-4cb6-b0ce-8052ca1d6da0",
"metadata" : {
"contentType" : "image/png",
"fileName" : "google-my-business-logo-png-transparent.png",
"size" : 66497,
"customProperties" : { }
}
}]
},
{
"to":{
Expand Down Expand Up @@ -78,6 +100,17 @@
]
},
"id":"d-0b51e8f77bf8450fae379e0639ca0d11"
}
},
"attachments" : [ {
"camunda.document.type" : "camunda",
"storeId" : "in-memory",
"documentId" : "2ea3bfcc-7683-4cb6-b0ce-8052ca1d6da0",
"metadata" : {
"contentType" : "image/png",
"fileName" : "google-my-business-logo-png-transparent.png",
"size" : 66497,
"customProperties" : { }
}
}]
}
]
Loading

0 comments on commit a6d9880

Please sign in to comment.