Skip to content

Commit

Permalink
feature(s3-connector): all features done
Browse files Browse the repository at this point in the history
  • Loading branch information
mathias-vandaele committed Dec 4, 2024
1 parent c6da398 commit f771d56
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ public class S3Executor {
private final S3Client s3Client;
private final Function<DocumentCreationRequest, Document> createDocument;

public S3Executor(
S3Request s3Request, Function<DocumentCreationRequest, Document> createDocument) {
this.s3Client =
S3Client.builder()
.credentialsProvider(CredentialsProviderSupportV2.credentialsProvider(s3Request))
.region(Region.of(s3Request.getConfiguration().region()))
.build();
public S3Executor(S3Client s3Client, Function<DocumentCreationRequest, Document> createDocument) {
this.s3Client = s3Client;
this.createDocument = createDocument;
}

public static S3Executor create(
S3Request s3Request, Function<DocumentCreationRequest, Document> createDocument) {
return new S3Executor(s3Request, createDocument);
return new S3Executor(
S3Client.builder()
.credentialsProvider(CredentialsProviderSupportV2.credentialsProvider(s3Request))
.region(Region.of(s3Request.getConfiguration().region()))
.build(),
createDocument);
}

public Object execute(S3Action s3Action) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.aws.s3;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.readString;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.camunda.connector.api.json.ConnectorsObjectMapperSupplier;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;

public class BaseTest {

public static Stream<String> loadUploadActionVariables() {
try {
return loadTestCasesFromResourceFile("src/test/resources/actions/uploadActionsExample.json");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static Stream<String> loadDownloadActionVariables() {
try {
return loadTestCasesFromResourceFile(
"src/test/resources/actions/downloadActionsExample.json");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static Stream<String> loadDeleteActionVariables() {
try {
return loadTestCasesFromResourceFile("src/test/resources/actions/deleteActionsExample.json");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@SuppressWarnings("unchecked")
protected static Stream<String> loadTestCasesFromResourceFile(final String fileWithTestCasesUri)
throws IOException {
final String cases = readString(new File(fileWithTestCasesUri).toPath(), UTF_8);
final ObjectMapper mapper = ConnectorsObjectMapperSupplier.getCopy();
var array = mapper.readValue(cases, ArrayList.class);
return array.stream()
.map(
value -> {
try {
return mapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
})
.map(Arguments::of);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.aws.s3;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import io.camunda.connector.aws.s3.core.S3Executor;
import io.camunda.connector.aws.s3.response.DeleteResponse;
import io.camunda.connector.aws.s3.response.DownloadResponse;
import io.camunda.connector.aws.s3.response.UploadResponse;
import io.camunda.connector.test.outbound.OutboundConnectorContextBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

class S3ConnectorFunctionTest extends BaseTest {

@ParameterizedTest
@MethodSource("loadUploadActionVariables")
void executeUploadActionReturnsCorrectResult(String variables) {

var bedrockConnectorFunction = new S3ConnectorFunction();
var context = OutboundConnectorContextBuilder.create().variables(variables).build();

var s3Executor = Mockito.mock(S3Executor.class);

try (MockedStatic<S3Executor> s3ExecutorMockedStatic = Mockito.mockStatic(S3Executor.class)) {
s3ExecutorMockedStatic.when(() -> S3Executor.create(any(), any())).thenReturn(s3Executor);
when(s3Executor.execute(any())).thenReturn(new UploadResponse("test", "test", "link"));
var response = bedrockConnectorFunction.execute(context);
Assertions.assertNotNull(response);
Assertions.assertInstanceOf(UploadResponse.class, response);
}
}

@ParameterizedTest
@MethodSource("loadDownloadActionVariables")
void executeDownloadActionReturnsCorrectResult(String variables) {

var bedrockConnectorFunction = new S3ConnectorFunction();
var context = OutboundConnectorContextBuilder.create().variables(variables).build();

var s3Executor = Mockito.mock(S3Executor.class);

try (MockedStatic<S3Executor> s3ExecutorMockedStatic = Mockito.mockStatic(S3Executor.class)) {
s3ExecutorMockedStatic.when(() -> S3Executor.create(any(), any())).thenReturn(s3Executor);
when(s3Executor.execute(any())).thenReturn(new DownloadResponse("test", "test", null, null));
var response = bedrockConnectorFunction.execute(context);
Assertions.assertNotNull(response);
Assertions.assertInstanceOf(DownloadResponse.class, response);
}
}

@ParameterizedTest
@MethodSource("loadDeleteActionVariables")
void executeDeleteActionReturnsCorrectResult(String variables) {

var bedrockConnectorFunction = new S3ConnectorFunction();
var context = OutboundConnectorContextBuilder.create().variables(variables).build();

var s3Executor = Mockito.mock(S3Executor.class);

try (MockedStatic<S3Executor> s3ExecutorMockedStatic = Mockito.mockStatic(S3Executor.class)) {
s3ExecutorMockedStatic.when(() -> S3Executor.create(any(), any())).thenReturn(s3Executor);
when(s3Executor.execute(any())).thenReturn(new DeleteResponse("test", "test"));
var response = bedrockConnectorFunction.execute(context);
Assertions.assertNotNull(response);
Assertions.assertInstanceOf(DeleteResponse.class, response);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* 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.aws.s3.core;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.camunda.connector.aws.s3.model.DeleteS3Action;
import io.camunda.connector.aws.s3.model.DownloadS3Action;
import io.camunda.connector.aws.s3.model.S3Action;
import io.camunda.connector.aws.s3.model.UploadS3Action;
import io.camunda.connector.aws.s3.response.DeleteResponse;
import io.camunda.connector.aws.s3.response.DownloadResponse;
import io.camunda.connector.aws.s3.response.UploadResponse;
import io.camunda.document.Document;
import io.camunda.document.store.DocumentCreationRequest;
import java.io.IOException;
import java.util.Base64;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

class S3ExecutorTest {

@Test
void executeDeleteAction() {
S3Client s3Client = mock(S3Client.class);
Function<DocumentCreationRequest, Document> function = doc -> mock(Document.class);
S3Executor executor = new S3Executor(s3Client, function);
S3Action s3Action = new DeleteS3Action("test", "key");

Object object = executor.execute(s3Action);

verify(s3Client, times(1)).deleteObject(any(DeleteObjectRequest.class));
assertInstanceOf(DeleteResponse.class, object);
}

@Test
void executeUploadAction() {
S3Client s3Client = mock(S3Client.class);
Function<DocumentCreationRequest, Document> function = doc -> mock(Document.class);
S3Executor executor = new S3Executor(s3Client, function);
Document document = mock(Document.class, RETURNS_DEEP_STUBS);
S3Action s3Action = new UploadS3Action("test", "key", document);

when(document.metadata().getSize()).thenReturn(42L);
when(document.metadata().getContentType()).thenReturn("application/octet-stream");

Object object = executor.execute(s3Action);

verify(s3Client, times(1)).putObject(any(PutObjectRequest.class), any(RequestBody.class));
assertInstanceOf(UploadResponse.class, object);
}

@Test
void executeDownloadAsDocumentAction() {

S3Client s3Client = mock(S3Client.class);
Function<DocumentCreationRequest, Document> function = doc -> mock(Document.class);
S3Executor executor = new S3Executor(s3Client, function);
ResponseInputStream<GetObjectResponse> responseInputStream = mock(ResponseInputStream.class);
GetObjectResponse getObjectResponse = mock(GetObjectResponse.class);
S3Action s3Action = new DownloadS3Action("test", "key", true);

when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(responseInputStream);
when(responseInputStream.response()).thenReturn(getObjectResponse);
when(getObjectResponse.contentType()).thenReturn("application/octet-stream");
Object object = executor.execute(s3Action);

verify(s3Client, times(1)).getObject(any(GetObjectRequest.class));
assertInstanceOf(DownloadResponse.class, object);
assertNull(((DownloadResponse) object).content());
assertNotNull(((DownloadResponse) object).document());
}

@Test
void executeDownloadAsTextContentAction() throws IOException {

S3Client s3Client = mock(S3Client.class);
Function<DocumentCreationRequest, Document> function = doc -> mock(Document.class);
S3Executor executor = new S3Executor(s3Client, function);
ResponseInputStream<GetObjectResponse> responseInputStream = mock(ResponseInputStream.class);
GetObjectResponse getObjectResponse = mock(GetObjectResponse.class);
S3Action s3Action = new DownloadS3Action("test", "key", false);

when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(responseInputStream);
when(responseInputStream.response()).thenReturn(getObjectResponse);
when(responseInputStream.readAllBytes()).thenReturn("Hello World".getBytes());
when(getObjectResponse.contentLength()).thenReturn(234L);
when(getObjectResponse.contentType()).thenReturn("text/plain");
Object object = executor.execute(s3Action);

verify(s3Client, times(1)).getObject(any(GetObjectRequest.class));
assertInstanceOf(DownloadResponse.class, object);
assertNotNull(((DownloadResponse) object).content());
assertNull(((DownloadResponse) object).document());
assertEquals("Hello World", ((DownloadResponse) object).content());
}

@Test
void executeDownloadAsJsonContentAction() throws IOException {

S3Client s3Client = mock(S3Client.class);
Function<DocumentCreationRequest, Document> function = doc -> mock(Document.class);
S3Executor executor = new S3Executor(s3Client, function);
ResponseInputStream<GetObjectResponse> responseInputStream = mock(ResponseInputStream.class);
GetObjectResponse getObjectResponse = mock(GetObjectResponse.class);
S3Action s3Action = new DownloadS3Action("test", "key", false);

when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(responseInputStream);
when(responseInputStream.response()).thenReturn(getObjectResponse);
when(responseInputStream.readAllBytes()).thenReturn("{ \"Hello\" : \"World\" }".getBytes());
when(getObjectResponse.contentLength()).thenReturn(234L);
when(getObjectResponse.contentType()).thenReturn("application/json");
Object object = executor.execute(s3Action);

verify(s3Client, times(1)).getObject(any(GetObjectRequest.class));
assertInstanceOf(DownloadResponse.class, object);
DownloadResponse downloadResponse = (DownloadResponse) object;
assertNotNull(downloadResponse.content());
assertNull(downloadResponse.document());
assertEquals("World", ((ObjectNode) downloadResponse.content()).get("Hello").asText());
}

@Test
void executeDownloadAsBase64BytesContentAction() throws IOException {

S3Client s3Client = mock(S3Client.class);
Function<DocumentCreationRequest, Document> function = doc -> mock(Document.class);
S3Executor executor = new S3Executor(s3Client, function);
ResponseInputStream<GetObjectResponse> responseInputStream = mock(ResponseInputStream.class);
GetObjectResponse getObjectResponse = mock(GetObjectResponse.class);
S3Action s3Action = new DownloadS3Action("test", "key", false);

when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(responseInputStream);
when(responseInputStream.response()).thenReturn(getObjectResponse);
when(responseInputStream.readAllBytes()).thenReturn("Hello".getBytes());
when(getObjectResponse.contentLength()).thenReturn(234L);
when(getObjectResponse.contentType()).thenReturn("application/octet-stream");
Object object = executor.execute(s3Action);

verify(s3Client, times(1)).getObject(any(GetObjectRequest.class));
assertInstanceOf(DownloadResponse.class, object);
DownloadResponse downloadResponse = (DownloadResponse) object;
assertNotNull(downloadResponse.content());
assertNull(downloadResponse.document());
assertEquals(
Base64.getEncoder().encodeToString("Hello".getBytes()), downloadResponse.content());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[
{
"action":{
"bucket":"connector-aws-s3-test",
"key":"test"
}, "configuration":{
"region":"eu-central-1"
},
"authentication":{
"type":"credentials",
"accessKey":"test",
"secretKey":"test"
}, "actionDiscriminator":"deleteObject"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"action":{
"bucket":"connector-aws-s3-test",
"key":"attachment",
"asFile":false
},
"configuration":{
"region":"eu-central-1"
},
"authentication":{
"type":"credentials",
"accessKey":"test",
"secretKey":"test"
},
"actionDiscriminator":"downloadObject"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"action":{
"bucket":"connector-aws-s3-test",
"key":"attachment",
"document":{
"storeId":"in-memory",
"documentId":"41d2a87f-f39c-4ddd-a116-18d2091cc695",
"metadata":{
"contentType":"text/plain", "size":41730,
"fileName":"test.txt"
},
"documentType":"camunda"
}
}, "configuration":{"region":"eu-central-1"},
"authentication":{
"type":"credentials",
"accessKey":"test",
"secretKey":"test"
}, "actionDiscriminator":"uploadObject"
}
]

0 comments on commit f771d56

Please sign in to comment.