This is an automated email from the ASF dual-hosted git repository.
zhfeng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new c614dc6 Fix #2745 Expand AWS S3 test coverage (#3077)
c614dc6 is described below
commit c614dc68f912514b36d8ff749ca9815acc18a067
Author: Amos Feng <[email protected]>
AuthorDate: Fri Sep 10 21:21:46 2021 +0800
Fix #2745 Expand AWS S3 test coverage (#3077)
* Fix #2745 Expand AWS S3 test coverage
* Multipart upload
* copyObject
* listBuckets
* deleteBucket
* downloadLink
* getObjectRange
* Fix multipart upload test
---
integration-test-groups/aws2/aws2-s3/pom.xml | 4 +
.../quarkus/component/aws2/Aws2S3Resource.java | 149 +++++++++++++++++++-
.../src/main/resources/application.properties | 1 -
.../camel/quarkus/component/aws2/Aws2S3Test.java | 155 +++++++++++++++++++++
integration-tests/aws2-grouped/pom.xml | 4 +
5 files changed, 307 insertions(+), 6 deletions(-)
diff --git a/integration-test-groups/aws2/aws2-s3/pom.xml
b/integration-test-groups/aws2/aws2-s3/pom.xml
index fe36bbe..f839478 100644
--- a/integration-test-groups/aws2/aws2-s3/pom.xml
+++ b/integration-test-groups/aws2/aws2-s3/pom.xml
@@ -52,6 +52,10 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
+ <artifactId>quarkus-resteasy-multipart</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
diff --git
a/integration-test-groups/aws2/aws2-s3/src/main/java/org/apache/camel/quarkus/component/aws2/Aws2S3Resource.java
b/integration-test-groups/aws2/aws2-s3/src/main/java/org/apache/camel/quarkus/component/aws2/Aws2S3Resource.java
index 8d7903f..e3794c1 100644
---
a/integration-test-groups/aws2/aws2-s3/src/main/java/org/apache/camel/quarkus/component/aws2/Aws2S3Resource.java
+++
b/integration-test-groups/aws2/aws2-s3/src/main/java/org/apache/camel/quarkus/component/aws2/Aws2S3Resource.java
@@ -16,19 +16,31 @@
*/
package org.apache.camel.quarkus.component.aws2;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.net.URI;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -37,6 +49,10 @@ import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.aws2.s3.AWS2S3Constants;
import org.apache.camel.component.aws2.s3.AWS2S3Operations;
import org.eclipse.microprofile.config.inject.ConfigProperty;
+import software.amazon.awssdk.core.ResponseInputStream;
+import software.amazon.awssdk.services.s3.model.Bucket;
+import software.amazon.awssdk.services.s3.model.GetObjectResponse;
+import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
import software.amazon.awssdk.services.s3.model.S3Object;
@Path("/aws2")
@@ -66,9 +82,13 @@ public class Aws2S3Resource {
@Path("s3/object/{key}")
@GET
@Produces(MediaType.TEXT_PLAIN)
- public String get(@PathParam("key") String key) throws Exception {
+ public String get(@PathParam("key") String key, @QueryParam("bucket")
String bucket) throws Exception {
+ if (bucket == null) {
+ bucket = bucketName;
+ }
+
return producerTemplate.requestBodyAndHeader(
- componentUri(AWS2S3Operations.getObject),
+ componentUri(bucket, AWS2S3Operations.getObject),
null,
AWS2S3Constants.KEY,
key,
@@ -97,18 +117,137 @@ public class Aws2S3Resource {
@Path("s3/object-keys")
@GET
@Produces(MediaType.APPLICATION_JSON)
- public List<String> objectKey() throws Exception {
+ public List<String> objectKey(@QueryParam("bucket") String bucket) throws
Exception {
+ if (bucket == null) {
+ bucket = bucketName;
+ }
+
final List<S3Object> objects = (List<S3Object>)
producerTemplate.requestBody(
- componentUri(AWS2S3Operations.listObjects),
+ componentUri(bucket, AWS2S3Operations.listObjects) +
"&autoCreateBucket=true",
null,
List.class);
return
objects.stream().map(S3Object::key).collect(Collectors.toList());
}
- private String componentUri(final AWS2S3Operations operation) {
+ @Path("s3/upload/{key}")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String upload(@PathParam("key") String key, String content) throws
Exception {
+ File file = File.createTempFile("aws2-", ".tmp");
+ Files.writeString(file.toPath(), content, StandardOpenOption.WRITE);
+ int partSize = 5 * 1024 * 1024;
+
+ producerTemplate.sendBodyAndHeader(
+ componentUri() + "?multiPartUpload=true&partSize=" + partSize
+ "&autoCreateBucket=true",
+ file,
+ AWS2S3Constants.KEY,
+ key);
+
+ return key;
+ }
+
+ @Path("s3/copy/{key}")
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response copyObject(@PathParam("key") String key,
+ @FormParam("dest_key") String dest_key, @FormParam("dest_bucket")
String dest_bucket) {
+ Map<String, Object> headers = new LinkedHashMap<>();
+ headers.put(AWS2S3Constants.KEY, key);
+ headers.put(AWS2S3Constants.DESTINATION_KEY, dest_key);
+ headers.put(AWS2S3Constants.BUCKET_DESTINATION_NAME, dest_bucket);
+
+ producerTemplate.sendBodyAndHeaders(
+ componentUri(AWS2S3Operations.copyObject),
+ null, headers);
+
+ return Response.noContent().build();
+ }
+
+ @Path("s3/bucket")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<String> listBuckets() throws Exception {
+ List<Bucket> buckets = (List<Bucket>) producerTemplate.requestBody(
+ componentUri(AWS2S3Operations.listBuckets),
+ null,
+ List.class);
+ return buckets.stream().map(Bucket::name).collect(Collectors.toList());
+ }
+
+ @Path("s3/bucket/{name}")
+ @DELETE
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response deleteBucket(@PathParam("name") String bucketName) {
+ try {
+ producerTemplate.sendBodyAndHeader(
+ componentUri(bucketName, AWS2S3Operations.deleteBucket),
+ null,
+ AWS2S3Constants.BUCKET_NAME,
+ bucketName);
+ } catch (NoSuchBucketException e) {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ return Response.noContent().build();
+ }
+
+ @Path("s3/downloadlink/{key}")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String downloadLink(@PathParam("key") String key,
@QueryParam("bucket") String bucket) {
+ if (bucket == null) {
+ bucket = bucketName;
+ }
+
+ String link = producerTemplate.requestBodyAndHeader(
+ componentUri(bucket, AWS2S3Operations.createDownloadLink),
+ null,
+ AWS2S3Constants.KEY,
+ key,
+ String.class);
+
+ return link;
+ }
+
+ @Path("s3/object/range/{key}")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String objectRange(@PathParam("key") String key,
+ @QueryParam("start") Integer start,
+ @QueryParam("end") Integer end) throws Exception {
+ Map<String, Object> headers = new LinkedHashMap<>();
+ headers.put(AWS2S3Constants.KEY, key);
+ headers.put(AWS2S3Constants.RANGE_START, start);
+ headers.put(AWS2S3Constants.RANGE_END, end);
+
+ ResponseInputStream<GetObjectResponse> s3Object =
producerTemplate.requestBodyAndHeaders(
+ componentUri(AWS2S3Operations.getObjectRange) +
"&autoCreateBucket=false",
+ null,
+ headers,
+ ResponseInputStream.class);
+
+ StringBuilder textBuilder = new StringBuilder();
+ try (Reader reader = new BufferedReader(
+ new InputStreamReader(s3Object,
Charset.forName(StandardCharsets.UTF_8.name())))) {
+ int c = 0;
+ while ((c = reader.read()) != -1) {
+ textBuilder.append((char) c);
+ }
+ }
+
+ return textBuilder.toString();
+ }
+
+ private String componentUri(String bucketName, final AWS2S3Operations
operation) {
return String.format("aws2-s3://%s?operation=%s", bucketName,
operation);
}
+ private String componentUri(final AWS2S3Operations operation) {
+ return componentUri(bucketName, operation);
+ }
+
private String componentUri() {
return String.format("aws2-s3://%s", bucketName);
}
diff --git
a/integration-test-groups/aws2/aws2-s3/src/main/resources/application.properties
b/integration-test-groups/aws2/aws2-s3/src/main/resources/application.properties
index 967305c..745bd72 100644
---
a/integration-test-groups/aws2/aws2-s3/src/main/resources/application.properties
+++
b/integration-test-groups/aws2/aws2-s3/src/main/resources/application.properties
@@ -14,7 +14,6 @@
## See the License for the specific language governing permissions and
## limitations under the License.
## ---------------------------------------------------------------------------
-
#
# Camel :: AWS2 options
#
diff --git
a/integration-test-groups/aws2/aws2-s3/src/test/java/org/apache/camel/quarkus/component/aws2/Aws2S3Test.java
b/integration-test-groups/aws2/aws2-s3/src/test/java/org/apache/camel/quarkus/component/aws2/Aws2S3Test.java
index 372818b..2113683 100644
---
a/integration-test-groups/aws2/aws2-s3/src/test/java/org/apache/camel/quarkus/component/aws2/Aws2S3Test.java
+++
b/integration-test-groups/aws2/aws2-s3/src/test/java/org/apache/camel/quarkus/component/aws2/Aws2S3Test.java
@@ -24,10 +24,12 @@ import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.apache.camel.quarkus.test.support.aws2.Aws2TestResource;
+import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
@QuarkusTest
@QuarkusTestResource(Aws2TestResource.class)
@@ -125,4 +127,157 @@ class Aws2S3Test {
}
}
+ @Test
+ public void upload() throws Exception {
+ final String oid = UUID.randomUUID().toString();
+ final String content = RandomStringUtils.randomAlphabetic(8 * 1024 *
1024);
+
+ RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .body(content)
+ .post("/aws2/s3/upload/" + oid)
+ .then()
+ .statusCode(200);
+
+ String result = RestAssured.get("/aws2/s3/object/" + oid)
+ .then()
+ .statusCode(200)
+ .extract().asString();
+
+ // strip the chuck-signature
+ result = result.replaceAll("\\s*[0-9]+;chunk-signature=\\w{64}\\s*",
"");
+ assertEquals(content, result);
+ }
+
+ @Test
+ public void copyObject() throws Exception {
+ final String oid1 = UUID.randomUUID().toString();
+ final String oid2 = UUID.randomUUID().toString();
+ final String blobContent = "Hello " + oid1;
+ final String bucket = "mycamel";
+
+ // Create
+ RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .body(blobContent)
+ .post("/aws2/s3/object/" + oid1)
+ .then()
+ .statusCode(201);
+
+ // Check the dest bucket does not contain oid2
+ final String[] objects = getAllObjects(bucket);
+ Assertions.assertTrue(Stream.of(objects).noneMatch(key ->
key.equals(oid2)));
+
+ // Copy
+ RestAssured.given()
+ .contentType(ContentType.URLENC)
+ .formParam("dest_key", oid2)
+ .formParam("dest_bucket", bucket)
+ .post("/aws2/s3/copy/" + oid1)
+ .then()
+ .statusCode(204);
+
+ // Verify the object
+ RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .get("/aws2/s3/object/" + oid2 + "?bucket=" + bucket)
+ .then()
+ .statusCode(200)
+ .body(is(blobContent));
+
+ }
+
+ @Test
+ void listBuckets() throws Exception {
+ final String[] buckets = getAllBuckets();
+
+ Assertions.assertTrue(Stream.of(buckets).anyMatch(key ->
key.startsWith("camel-quarkus")));
+ }
+
+ @Test
+ void deleteBucket() throws Exception {
+ final String bucket = "mycamel-delete";
+
+ String[] objects = getAllObjects(bucket);
+ Assertions.assertTrue(objects.length == 0);
+
+ String[] buckets = getAllBuckets();
+ Assertions.assertTrue(Stream.of(buckets).anyMatch(key ->
key.equals("mycamel-delete")));
+
+ RestAssured.delete("/aws2/s3/bucket/" + bucket)
+ .then()
+ .statusCode(204);
+
+ buckets = getAllBuckets();
+ Assertions.assertTrue(Stream.of(buckets).noneMatch(key ->
key.equals("mycamel-delete")));
+ }
+
+ @Test
+ public void downloadLink() throws Exception {
+ final String oid = UUID.randomUUID().toString();
+ final String blobContent = "Hello " + oid;
+
+ // Create
+ createObject(oid, blobContent);
+
+ // Download link
+ RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .get("/aws2/s3/downloadlink/" + oid)
+ .then()
+ .statusCode(200);
+
+ }
+
+ @Test
+ public void objectRange() {
+ final String oid = UUID.randomUUID().toString();
+ final String blobContent = "Hello " + oid;
+
+ // Create
+ createObject(oid, blobContent);
+
+ // Object range
+ RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .param("start", "0").param("end", "4")
+ .get("/aws2/s3/object/range/" + oid)
+ .then()
+ .statusCode(200)
+ .body(is("Hello"));
+ }
+
+ private void createObject(String oid, String blobContent) {
+ RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .body(blobContent)
+ .post("/aws2/s3/object/" + oid)
+ .then()
+ .statusCode(201);
+ }
+
+ private String[] getAllObjects(String bucket) {
+ final String[] objects = RestAssured.given()
+ .param("bucket", bucket)
+ .get("/aws2/s3/object-keys")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body().as(String[].class);
+
+ return objects;
+ }
+
+ private String[] getAllBuckets() {
+ String[] buckets = RestAssured.given()
+ .contentType(ContentType.TEXT)
+ .get("/aws2/s3/bucket")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body().as(String[].class);
+
+ return buckets;
+ }
+
}
diff --git a/integration-tests/aws2-grouped/pom.xml
b/integration-tests/aws2-grouped/pom.xml
index 04cf58b..cfe3e10 100644
--- a/integration-tests/aws2-grouped/pom.xml
+++ b/integration-tests/aws2-grouped/pom.xml
@@ -56,6 +56,10 @@
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-resteasy-multipart</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-aws2-cw</artifactId>
</dependency>