This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch 2.7.x in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit 8d003ace4628bef29a3258d7934aebe11508fa46 Author: James Netherton <[email protected]> AuthorDate: Tue Feb 22 15:10:04 2022 +0000 Increase azure-storage-blob extension test coverage Fixes #3561 --- integration-test-groups/azure/README.adoc | 3 +- .../azure/azure-eventhubs/pom.xml | 2 +- integration-test-groups/azure/azure-resources.sh | 9 +- .../azure/azure-storage-blob/pom.xml | 48 ++ .../storage/blob/it/AzureStorageBlobProducers.java | 49 ++ .../storage/blob/it/AzureStorageBlobResource.java | 394 ++++++++++++++-- .../storage/blob/it/AzureStorageBlobRoutes.java | 125 ++++++ .../storage/blob/it/AzureStorageBlobTest.java | 500 +++++++++++++++++++-- .../test/mock/backend/MockBackendDisabled.java | 30 ++ integration-tests/azure-grouped/pom.xml | 41 +- 10 files changed, 1111 insertions(+), 90 deletions(-) diff --git a/integration-test-groups/azure/README.adoc b/integration-test-groups/azure/README.adoc index 33c21ec..7115205 100644 --- a/integration-test-groups/azure/README.adoc +++ b/integration-test-groups/azure/README.adoc @@ -15,11 +15,12 @@ Prerequisites: * A https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create?toc=%2Fazure%2Fstorage%2Fblobs%2Ftoc.json&tabs=azure-portal[general-purpose v2 Azure storage account] and https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-portal[create a container] +* The https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-change-feed?tabs=azure-portal#enable-and-disable-the-change-feed[change feed] is enabled on your storage account * View the https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal#view-account-access-keys[account keys] and set the following environment variables: * An https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-create[Azure Event Hub] * An https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-get-connection-string[Event Hubs connection string] -To create all of the above, you can use `azure-resources.sh` script as follows: +To create all of the above, you can use `azure-resources.sh` script as follows. Ensure that you have installed the https://docs.microsoft.com/en-us/cli/azure/[Azure CLI] beforehand: [source,shell] ---- diff --git a/integration-test-groups/azure/azure-eventhubs/pom.xml b/integration-test-groups/azure/azure-eventhubs/pom.xml index a87ed56..838b66d 100644 --- a/integration-test-groups/azure/azure-eventhubs/pom.xml +++ b/integration-test-groups/azure/azure-eventhubs/pom.xml @@ -49,7 +49,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-jackson</artifactId> + <artifactId>quarkus-resteasy-jsonb</artifactId> </dependency> <!-- test dependencies --> diff --git a/integration-test-groups/azure/azure-resources.sh b/integration-test-groups/azure/azure-resources.sh index 8b7a4b6..8bdcbaa 100755 --- a/integration-test-groups/azure/azure-resources.sh +++ b/integration-test-groups/azure/azure-resources.sh @@ -16,6 +16,13 @@ # limitations under the License. # +if ! which az > /dev/null 2>&1; then + echo "$(basename $0) requires the Azure CLI." + echo + echo "https://docs.microsoft.com/en-us/cli/azure/" + echo + exit 1 +fi suffix="$(az ad signed-in-user show --query displayName -o tsv | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]' | cut -c-12)" suffix="${suffix}4" @@ -33,6 +40,7 @@ function createResources() { az group create --name ${RESOURCE_GROUP} --location ${ZONE} az storage account create --name ${AZURE_STORAGE_ACCOUNT_NAME} --resource-group ${RESOURCE_GROUP} --location ${ZONE} --sku Standard_LRS --kind StorageV2 + az storage account blob-service-properties update --enable-change-feed true --delete-retention-days 1 -n ${AZURE_STORAGE_ACCOUNT_NAME} -g ${RESOURCE_GROUP} SUBSCRIPTION_ID="$(az account list --query '[0].id' -o tsv)" USER_ID="$(az ad signed-in-user show --query objectId -o tsv)" @@ -79,4 +87,3 @@ delete) echo "Deleting Azure resources" *) echo "usage: $0 [create|delete]" ;; esac - diff --git a/integration-test-groups/azure/azure-storage-blob/pom.xml b/integration-test-groups/azure/azure-storage-blob/pom.xml index e5837f2..0c19ae3 100644 --- a/integration-test-groups/azure/azure-storage-blob/pom.xml +++ b/integration-test-groups/azure/azure-storage-blob/pom.xml @@ -36,9 +36,21 @@ <artifactId>camel-quarkus-azure-storage-blob</artifactId> </dependency> <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-seda</artifactId> + </dependency> + <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy-jsonb</artifactId> + </dependency> <!-- test dependencies --> <dependency> @@ -52,6 +64,16 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-integration-test-support</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-integration-tests-support-azure</artifactId> <scope>test</scope> @@ -108,6 +130,32 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-seda-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> </dependencies> </profile> <profile> diff --git a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobProducers.java b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobProducers.java new file mode 100644 index 0000000..dfd65be --- /dev/null +++ b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobProducers.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.quarkus.component.azure.storage.blob.it; + +import javax.inject.Named; + +import com.azure.core.http.policy.HttpLogDetailLevel; +import com.azure.core.http.policy.HttpLogOptions; +import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.common.StorageSharedKeyCredential; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +public class AzureStorageBlobProducers { + + @ConfigProperty(name = "azure.storage.account-name") + String azureStorageAccountName; + + @ConfigProperty(name = "azure.storage.account-key") + String azureStorageAccountKey; + + @ConfigProperty(name = "azure.blob.service.url") + String azureBlobServiceUrl; + + @Named("azureBlobServiceClient") + public BlobServiceClient createBlobClient() throws Exception { + StorageSharedKeyCredential credentials = new StorageSharedKeyCredential(azureStorageAccountName, + azureStorageAccountKey); + return new BlobServiceClientBuilder() + .endpoint(azureBlobServiceUrl) + .credential(credentials) + .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS).setPrettyPrintBody(true)) + .buildClient(); + } +} diff --git a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java index 95e7bb0..790cbb1 100644 --- a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java +++ b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobResource.java @@ -16,105 +16,407 @@ */ package org.apache.camel.quarkus.component.azure.storage.blob.it; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; -import javax.inject.Named; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PATCH; 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; -import com.azure.core.http.policy.HttpLogDetailLevel; -import com.azure.core.http.policy.HttpLogOptions; -import com.azure.storage.blob.BlobServiceClient; -import com.azure.storage.blob.BlobServiceClientBuilder; -import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.storage.blob.changefeed.models.BlobChangefeedEvent; +import com.azure.storage.blob.changefeed.models.BlobChangefeedEventType; +import com.azure.storage.blob.models.BlobContainerItem; +import com.azure.storage.blob.models.BlobItem; +import com.azure.storage.blob.models.BlobStorageException; +import com.azure.storage.blob.models.Block; +import com.azure.storage.blob.models.BlockList; +import com.azure.storage.blob.models.BlockListType; +import com.azure.storage.blob.models.PageList; +import com.azure.storage.blob.models.PageRange; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.ConsumerTemplate; import org.apache.camel.Exchange; +import org.apache.camel.Message; +import org.apache.camel.Processor; import org.apache.camel.ProducerTemplate; -import org.apache.camel.component.azure.storage.blob.BlobOperationsDefinition; +import org.apache.camel.component.azure.storage.blob.BlobBlock; +import org.apache.camel.component.azure.storage.blob.BlobConstants; +import org.apache.camel.quarkus.core.util.FileUtils; import org.eclipse.microprofile.config.inject.ConfigProperty; @Path("/azure-storage-blob") @ApplicationScoped public class AzureStorageBlobResource { - private static final String BLOB_NAME = "test"; @Inject ProducerTemplate producerTemplate; - @ConfigProperty(name = "azure.storage.account-name") - String azureStorageAccountName; + @Inject + ConsumerTemplate consumerTemplate; - @ConfigProperty(name = "azure.storage.account-key") - String azureStorageAccountKey; + @Inject + CamelContext context; - @ConfigProperty(name = "azure.blob.service.url") - String azureBlobServiceUrl; + @ConfigProperty(name = "azure.storage.account-name") + public String azureStorageAccountName; @ConfigProperty(name = "azure.blob.container.name") - String azureBlobContainerName; - - @javax.enterprise.inject.Produces - @Named("azureBlobServiceClient") - public BlobServiceClient createBlobClient() throws Exception { - StorageSharedKeyCredential credentials = new StorageSharedKeyCredential(azureStorageAccountName, - azureStorageAccountKey); - return new BlobServiceClientBuilder() - .endpoint(azureBlobServiceUrl) - .credential(credentials) - .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS).setPrettyPrintBody(true)) - .buildClient(); - } + public String azureBlobContainerName; @Path("/blob/create") @POST @Consumes(MediaType.TEXT_PLAIN) - public Response createBlob(String message) throws Exception { - producerTemplate.sendBody(componentUri(BlobOperationsDefinition.uploadBlockBlob), - message); - return Response.created(new URI("https://camel.apache.org/")).build(); + public Response createBlob(String content) throws Exception { + Exchange exchange = producerTemplate.request("direct:create", new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.getMessage().setBody(content); + } + }); + + if (!exchange.isFailed()) { + Message message = exchange.getMessage(); + return Response.created(new URI("https://camel.apache.org/")) + .entity(message.getHeader(BlobConstants.E_TAG)) + .build(); + } + + return Response.serverError().build(); } @Path("/blob/read") @GET @Produces(MediaType.TEXT_PLAIN) - public String readBlob() throws Exception { + public String readBlob(@QueryParam("containerName") String containerName) { + if (containerName == null) { + containerName = azureBlobContainerName; + } + + Map<String, Object> headers = new HashMap<>(); + headers.put(Exchange.CHARSET_NAME, StandardCharsets.UTF_8.name()); + headers.put(BlobConstants.BLOB_CONTAINER_NAME, containerName); + return producerTemplate.requestBodyAndHeaders("direct:read", null, headers, String.class); + } + + @Path("/blob/read/bytes") + @GET + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public byte[] readBlobBytes() { return producerTemplate.requestBodyAndHeader( - componentUri(BlobOperationsDefinition.getBlob), - null, Exchange.CHARSET_NAME, StandardCharsets.UTF_8.name(), String.class); + "direct:read", + null, Exchange.CHARSET_NAME, StandardCharsets.UTF_8.name(), byte[].class); + } + + @Path("/blob/list") + @GET + @Produces(MediaType.APPLICATION_JSON) + @SuppressWarnings("unchecked") + public JsonObject listBlobs() { + JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); + JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + + List<BlobItem> blobs = producerTemplate.requestBody("direct:list", null, List.class); + blobs.stream() + .map(blobItem -> objectBuilder.add("name", blobItem.getName())) + .forEach(arrayBuilder::add); + + objectBuilder.add("blobs", arrayBuilder); + + return objectBuilder.build(); } @Path("/blob/update") @PATCH @Consumes(MediaType.TEXT_PLAIN) - public Response updateBlob(String message) throws Exception { - producerTemplate.sendBody( - componentUri(BlobOperationsDefinition.uploadBlockBlob), - message); + public Response updateBlob(String message) { + producerTemplate.sendBody("direct:update", message); return Response.ok().build(); } @Path("/blob/delete") @DELETE - public Response deleteBlob() throws Exception { - producerTemplate.sendBody( - componentUri(BlobOperationsDefinition.deleteBlob), - null); + public Response deleteBlob() { + try { + producerTemplate.sendBody("direct:delete", null); + } catch (CamelExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof BlobStorageException) { + BlobStorageException bse = (BlobStorageException) cause; + return Response.status(bse.getStatusCode()).build(); + } + } return Response.noContent().build(); } - private String componentUri(final BlobOperationsDefinition operation) { - return String.format("azure-storage-blob://%s/%s?blobServiceClient=#azureBlobServiceClient&operation=%s&blobName=%s", - azureStorageAccountName, azureBlobContainerName, - operation.name(), BLOB_NAME); + @Path("/blob/download") + @GET + public Response downloadBlob() { + File file = producerTemplate.requestBody("direct:download", null, File.class); + String downloadPath = FileUtils.nixifyPath(file.getAbsolutePath()); + return Response.ok(downloadPath).build(); + } + + @Path("/blob/download/link") + @GET + public Response downloadLink() { + String link = producerTemplate.requestBody("direct:downloadLink", null, String.class); + return Response.ok(link).build(); + } + + @Path("/block/blob/create") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public Response createBlockBlob(String content) throws Exception { + producerTemplate.sendBody("direct:uploadBlockBlob", content); + return Response.created(new URI("https://camel.apache.org/")).build(); + } + + /** + * Note: The 'blob block' naming is retained here instead of the alternative 'block blob' naming used + * for other operations. Both the Camel and official Azure documentation have this inconsistency. + */ + @Path("/blob/block/list") + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonObject readBlobBlockList(@QueryParam("blockListType") String blockListType) { + BlockListType listType = BlockListType.valueOf(blockListType.toUpperCase()); + BlockList list = producerTemplate.requestBodyAndHeader( + "direct:readBlobBlocks", + null, BlobConstants.BLOCK_LIST_TYPE, listType, BlockList.class); + + JsonObjectBuilder builder = Json.createObjectBuilder(); + if (listType.equals(BlockListType.ALL) || listType.equals(BlockListType.UNCOMMITTED)) { + extractBlockNames(builder, list.getUncommittedBlocks(), BlockListType.UNCOMMITTED); + } + + if (listType.equals(BlockListType.ALL) || listType.equals(BlockListType.COMMITTED)) { + extractBlockNames(builder, list.getCommittedBlocks(), BlockListType.COMMITTED); + } + + return builder.build(); + } + + @Path("/block/blob/stage") + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Boolean stageBlockBlobs(List<String> blockContent) { + List<BlobBlock> blocks = blockContent.stream() + .map(String::getBytes) + .map(ByteArrayInputStream::new) + .map(inputStream -> { + try { + return BlobBlock.createBlobBlock(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toList()); + return producerTemplate.requestBody("direct:stageBlockBlob", blocks, Boolean.class); + } + + @Path("/block/blob/commit") + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Boolean commitBlockBlobs(List<String> blockNames) { + List<Block> blocks = blockNames.stream() + .map(name -> { + Block block = new Block(); + block.setName(name); + return block; + }) + .collect(Collectors.toList()); + + return producerTemplate.requestBody("direct:commitBlockBlob", blocks, Boolean.class); + } + + @Path("/append/blob/create") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public Response createAppendBlob(String content) throws URISyntaxException { + producerTemplate.sendBody("direct:createAppendBlob", content); + return Response.created(new URI("https://camel.apache.org/")).build(); + } + + @Path("/append/blob/commit") + @POST + @Produces(MediaType.TEXT_PLAIN) + public Boolean commitAppendBlob(String contentToAppend) { + byte[] bytes = contentToAppend.getBytes(StandardCharsets.UTF_8); + return producerTemplate.requestBody("direct:commitAppendBlob", new ByteArrayInputStream(bytes), Boolean.class); + } + + @Path("/page/blob/create") + @POST + public Response createPageBlob() throws URISyntaxException { + producerTemplate.sendBody("direct:createPageBlob", null); + return Response.created(new URI("https://camel.apache.org/")).build(); + } + + @Path("/page/blob/upload") + @POST + @Produces(MediaType.TEXT_PLAIN) + public Boolean uploadPageBlob(@QueryParam("pageStart") int start, @QueryParam("pageEnd") int end) { + byte[] dataBytes = new byte[end + 1]; + new Random().nextBytes(dataBytes); + InputStream dataStream = new ByteArrayInputStream(dataBytes); + PageRange pageRange = new PageRange().setStart(start).setEnd(end); + return producerTemplate.requestBodyAndHeader("direct:uploadPageBlob", dataStream, + BlobConstants.PAGE_BLOB_RANGE, pageRange, Boolean.class); + } + + @Path("/page/blob/resize") + @POST + @Produces(MediaType.TEXT_PLAIN) + public Boolean resizePageBlob(@QueryParam("pageStart") int start, @QueryParam("pageEnd") int end) { + PageRange pageRange = new PageRange().setStart(start).setEnd(end); + return producerTemplate.requestBodyAndHeader("direct:resizePageBlob", null, + BlobConstants.PAGE_BLOB_RANGE, pageRange, Boolean.class); + } + + @Path("/page/blob/clear") + @POST + @Produces(MediaType.TEXT_PLAIN) + public Boolean clearPageBlob(@QueryParam("pageStart") int start, @QueryParam("pageEnd") int end) { + PageRange pageRange = new PageRange().setStart(start).setEnd(end); + return producerTemplate.requestBodyAndHeader("direct:clearPageBlob", null, + BlobConstants.PAGE_BLOB_RANGE, pageRange, Boolean.class); + } + + @Path("/page/blob") + @GET + @Produces(MediaType.APPLICATION_JSON) + public JsonObject getPageBlobRanges(@QueryParam("pageStart") int start, @QueryParam("pageEnd") int end) { + PageRange pageRange = new PageRange().setStart(start).setEnd(end); + PageList pageList = producerTemplate.requestBodyAndHeader("direct:getPageBlobRanges", null, + BlobConstants.PAGE_BLOB_RANGE, pageRange, PageList.class); + + JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); + JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + pageList.getPageRange() + .stream() + .map(pr -> Json.createObjectBuilder() + .add("start", pr.getStart()) + .add("end", pr.getEnd()) + .build()) + .forEach(arrayBuilder::add); + + objectBuilder.add("ranges", arrayBuilder.build()); + return objectBuilder.build(); + } + + @Path("/blob/container") + @POST + public Response createBlobContainer(@QueryParam("containerName") String containerName) throws Exception { + producerTemplate.sendBodyAndHeader("direct:createBlobContainer", null, BlobConstants.BLOB_CONTAINER_NAME, + containerName); + return Response.created(new URI("https://camel.apache.org/")).build(); + } + + @Path("/blob/container") + @GET + @Produces(MediaType.APPLICATION_JSON) + @SuppressWarnings("unchecked") + public JsonObject listBlobContainers() throws Exception { + JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); + JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + + List<BlobContainerItem> containers = producerTemplate.requestBody("direct:listBlobContainers", null, List.class); + containers.stream() + .map(container -> Json.createObjectBuilder() + .add("name", container.getName()) + .build()) + .forEach(arrayBuilder::add); + + objectBuilder.add("containers", arrayBuilder.build()); + return objectBuilder.build(); } + @Path("/blob/container") + @DELETE + public void deleteBlobContainer(@QueryParam("containerName") String containerName) { + producerTemplate.sendBodyAndHeader("direct:deleteBlobContainer", null, BlobConstants.BLOB_CONTAINER_NAME, + containerName); + } + + @Path("/blob/copy") + @POST + @Produces(MediaType.TEXT_PLAIN) + public Response copyBlob(@QueryParam("containerName") String containerName) { + Map<String, Object> headers = new HashMap<>(); + headers.put(BlobConstants.BLOB_CONTAINER_NAME, containerName); + headers.put(BlobConstants.BLOB_NAME, AzureStorageBlobRoutes.BLOB_NAME); + headers.put(BlobConstants.SOURCE_BLOB_CONTAINER_NAME, azureBlobContainerName); + headers.put(BlobConstants.SOURCE_BLOB_ACCOUNT_NAME, azureStorageAccountName); + String result = producerTemplate.requestBodyAndHeaders("direct:copy", null, headers, String.class); + return Response.ok(result).build(); + } + + @Path("/changes") + @GET + @Produces(MediaType.TEXT_PLAIN) + @SuppressWarnings("unchecked") + public boolean getChangeFeed( + @QueryParam("startTime") String startTime, + @QueryParam("endTime") String endTime, + @QueryParam("etag") String eTag) { + Map<String, Object> headers = new HashMap<>(); + headers.put(BlobConstants.BLOB_NAME, AzureStorageBlobRoutes.BLOB_NAME); + headers.put(BlobConstants.CHANGE_FEED_START_TIME, OffsetDateTime.parse(startTime)); + headers.put(BlobConstants.CHANGE_FEED_END_TIME, OffsetDateTime.parse(endTime)); + + List<BlobChangefeedEvent> events = producerTemplate.requestBodyAndHeaders("direct:getChangeFeed", null, headers, + List.class); + return events.stream() + .filter(event -> event.getEventType().equals(BlobChangefeedEventType.BLOB_CREATED)) + .anyMatch(event -> event.getData().getETag().equals(eTag)); + } + + @Path("/consumed/blobs") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getConsumedBlobs() { + return consumerTemplate.receiveBody("seda:blobs", 10000, String.class); + } + + @POST + @Path("consumer/{enable}") + public void mangeBlobConsumer(@PathParam("enable") boolean enable) throws Exception { + if (enable) { + context.getRouteController().startRoute("blob-consumer"); + } else { + context.getRouteController().stopRoute("blob-consumer"); + } + } + + private void extractBlockNames(JsonObjectBuilder builder, List<Block> blocks, BlockListType listType) { + JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + blocks.stream().map(Block::getName).forEach(arrayBuilder::add); + builder.add(listType.toString(), arrayBuilder); + } } diff --git a/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobRoutes.java b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobRoutes.java new file mode 100644 index 0000000..a1de8d5 --- /dev/null +++ b/integration-test-groups/azure/azure-storage-blob/src/main/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobRoutes.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.quarkus.component.azure.storage.blob.it; + +import javax.enterprise.context.ApplicationScoped; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.azure.storage.blob.BlobConstants; +import org.apache.camel.component.azure.storage.blob.BlobOperationsDefinition; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@ApplicationScoped +public class AzureStorageBlobRoutes extends RouteBuilder { + + public static final String BLOB_NAME = "test"; + + @ConfigProperty(name = "azure.storage.account-name") + public String azureStorageAccountName; + + @ConfigProperty(name = "azure.blob.container.name") + public String azureBlobContainerName; + + @ConfigProperty(name = "azure.storage.account-key") + public String azureStorageAccountKey; + + @Override + public void configure() throws Exception { + fromF("azure-storage-blob://%s/%s", azureStorageAccountName, azureBlobContainerName) + .id("blob-consumer") + .autoStartup(false) + .to("seda:blobs"); + + from("direct:create") + .to(componentUri(BlobOperationsDefinition.uploadBlockBlob)); + + from("direct:read") + .to(componentUri(BlobOperationsDefinition.getBlob)); + + from("direct:update") + .to(componentUri(BlobOperationsDefinition.uploadBlockBlob)); + + from("direct:delete") + .to(componentUri(BlobOperationsDefinition.deleteBlob)); + + from("direct:list") + .to(componentUri(BlobOperationsDefinition.listBlobs)); + + from("direct:download") + .to(componentUri(BlobOperationsDefinition.downloadBlobToFile) + "&fileDir=target"); + + from("direct:copy") + .to(componentUri(BlobOperationsDefinition.copyBlob) + "&sourceBlobAccessKey=RAW(" + + azureStorageAccountKey + ")"); + + from("direct:downloadLink") + .to(componentUri(BlobOperationsDefinition.downloadLink)) + .setBody().header(BlobConstants.DOWNLOAD_LINK); + + from("direct:uploadBlockBlob") + .to(componentUri(BlobOperationsDefinition.uploadBlockBlob)); + + from("direct:stageBlockBlob") + .to(componentUri(BlobOperationsDefinition.stageBlockBlobList)); + + from("direct:commitBlockBlob") + .to(componentUri(BlobOperationsDefinition.commitBlobBlockList)); + + from("direct:readBlobBlocks") + .to(componentUri(BlobOperationsDefinition.getBlobBlockList)); + + from("direct:createAppendBlob") + .to(componentUri(BlobOperationsDefinition.createAppendBlob)); + + from("direct:commitAppendBlob") + .to(componentUri(BlobOperationsDefinition.commitAppendBlob)); + + from("direct:createPageBlob") + .to(componentUri(BlobOperationsDefinition.createPageBlob)); + + from("direct:uploadPageBlob") + .to(componentUri(BlobOperationsDefinition.uploadPageBlob)); + + from("direct:resizePageBlob") + .to(componentUri(BlobOperationsDefinition.resizePageBlob)); + + from("direct:clearPageBlob") + .to(componentUri(BlobOperationsDefinition.clearPageBlob)); + + from("direct:getPageBlobRanges") + .to(componentUri(BlobOperationsDefinition.getPageBlobRanges)); + + from("direct:getChangeFeed") + .toF(componentUri(BlobOperationsDefinition.getChangeFeed)); + + from("direct:createBlobContainer") + .to(componentUri(BlobOperationsDefinition.createBlobContainer)); + + from("direct:listBlobContainers") + .to(componentUri(BlobOperationsDefinition.listBlobContainers)); + + from("direct:deleteBlobContainer") + .to(componentUri(BlobOperationsDefinition.deleteBlobContainer)); + } + + private String componentUri(final BlobOperationsDefinition operation) { + return String.format("azure-storage-blob://%s/%s?operation=%s&blobName=%s", + azureStorageAccountName, + azureBlobContainerName, + operation.name(), BLOB_NAME); + } +} diff --git a/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java b/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java index de63caf..6594824 100644 --- a/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java +++ b/integration-test-groups/azure/azure-storage-blob/src/test/java/org/apache/camel/quarkus/component/azure/storage/blob/it/AzureStorageBlobTest.java @@ -16,40 +16,64 @@ */ package org.apache.camel.quarkus.component.azure.storage.blob.it; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + import com.azure.core.http.policy.HttpLogDetailLevel; import com.azure.core.http.policy.HttpLogOptions; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.models.BlockListType; import com.azure.storage.common.StorageSharedKeyCredential; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import io.restassured.http.ContentType; +import io.restassured.path.json.JsonPath; +import org.apache.camel.quarkus.test.EnabledIf; +import org.apache.camel.quarkus.test.mock.backend.MockBackendDisabled; import org.apache.camel.quarkus.test.support.azure.AzureStorageTestResource; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.org.awaitility.Awaitility; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.StringEndsWith.endsWith; +import static org.hamcrest.text.MatchesPattern.matchesPattern; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; @QuarkusTest @QuarkusTestResource(AzureStorageTestResource.class) class AzureStorageBlobTest { + private static final String BLOB_CONTENT = "Hello Camel Quarkus Azure Blob"; + @BeforeAll static void beforeAll() { - blobContainer().create(); + getClient().create(); } @AfterAll static void afterAll() { - blobContainer().delete(); + getClient().delete(); } - private static BlobContainerClient blobContainer() { + private static BlobContainerClient getClient() { final Config config = ConfigProvider.getConfig(); final String azureStorageAccountName = config.getValue("azure.storage.account-name", String.class); @@ -63,47 +87,443 @@ class AzureStorageBlobTest { .credential(credentials) .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS).setPrettyPrintBody(true)) .buildClient(); - BlobContainerClient blobContainer = client - .getBlobContainerClient(config.getValue("azure.blob.container.name", String.class)); - return blobContainer; + + String containerName = config.getValue("azure.blob.container.name", String.class); + return client.getBlobContainerClient(containerName); } @Test public void crud() { - String blobContent = "Hello Camel Quarkus Azure Blob"; - - // Create - RestAssured.given() - .contentType(ContentType.TEXT) - .body(blobContent) - .post("/azure-storage-blob/blob/create") - .then() - .statusCode(201); - - // Read - RestAssured.get("/azure-storage-blob/blob/read") - .then() - .statusCode(200) - .body(is(blobContent)); - - // Update - String updatedContent = blobContent + " updated"; - RestAssured.given() - .contentType(ContentType.TEXT) - .body(updatedContent) - .patch("/azure-storage-blob/blob/update") - .then() - .statusCode(200); - - RestAssured.get("/azure-storage-blob/blob/read") - .then() - .statusCode(200) - .body(is(updatedContent)); - - // Delete - RestAssured.delete("/azure-storage-blob/blob/delete") - .then() - .statusCode(204); + try { + // Create + RestAssured.given() + .contentType(ContentType.TEXT) + .body(BLOB_CONTENT) + .post("/azure-storage-blob/blob/create") + .then() + .statusCode(201); + + // Read + RestAssured.get("/azure-storage-blob/blob/read") + .then() + .statusCode(200) + .body(is(BLOB_CONTENT)); + + // List + RestAssured.get("/azure-storage-blob/blob/list") + .then() + .statusCode(200) + .body("blobs[0].name", is(AzureStorageBlobRoutes.BLOB_NAME)); + + // Update + String updatedContent = BLOB_CONTENT + " updated"; + RestAssured.given() + .contentType(ContentType.TEXT) + .body(updatedContent) + .patch("/azure-storage-blob/blob/update") + .then() + .statusCode(200); + + RestAssured.get("/azure-storage-blob/blob/read") + .then() + .statusCode(200) + .body(is(updatedContent)); + } finally { + // Delete + RestAssured.delete("/azure-storage-blob/blob/delete") + .then() + .statusCode(204); + } + } + + @Test + public void download() throws IOException { + Path path = null; + try { + // Create + RestAssured.given() + .contentType(ContentType.TEXT) + .body(BLOB_CONTENT) + .post("/azure-storage-blob/blob/create") + .then() + .statusCode(201); + + // Download file + String downloadPath = RestAssured.get("/azure-storage-blob/blob/download") + .then() + .statusCode(200) + .body(endsWith("target/test")) + .extract() + .body() + .asString(); + + path = Paths.get(downloadPath); + assertEquals(BLOB_CONTENT, Files.readString(path)); + + // Download link + RestAssured.get("/azure-storage-blob/blob/download/link") + .then() + .statusCode(200) + .body(matchesPattern("^(https?)://.*/test.*")); + } finally { + if (path != null) { + try { + Files.deleteIfExists(path); + } catch (IOException e) { + // Ignore + } + } + + // Delete + RestAssured.delete("/azure-storage-blob/blob/delete") + .then() + .statusCode(204); + } + } + + @Test + public void blockBlobStageCommit() { + try { + List<String> blockContent = Arrays.asList(BLOB_CONTENT.split(" ")); + + // Stage blocks + RestAssured.given() + .contentType(ContentType.JSON) + .body(blockContent) + .post("/azure-storage-blob/block/blob/stage") + .then() + .statusCode(200) + .body(is("true")); + + // Verify blocks uncommitted + JsonPath json = RestAssured.given() + .queryParam("blockListType", BlockListType.UNCOMMITTED) + .get("/azure-storage-blob/blob/block/list") + .then() + .statusCode(200) + .extract() + .body() + .jsonPath(); + + List<String> uncommittedBlocks = json.getList(BlockListType.UNCOMMITTED.toString()); + assertNotNull(uncommittedBlocks); + assertEquals(blockContent.size(), uncommittedBlocks.size()); + + // Commit blocks + RestAssured.given() + .contentType(ContentType.JSON) + .body(uncommittedBlocks) + .post("/azure-storage-blob/block/blob/commit") + .then() + .statusCode(200) + .body(is("true")); + + // Verify blocks committed + json = RestAssured.given() + .queryParam("blockListType", BlockListType.COMMITTED) + .get("/azure-storage-blob/blob/block/list") + .then() + .statusCode(200) + .extract() + .body() + .jsonPath(); + + List<String> committedBlocks = json.getList(BlockListType.COMMITTED.toString()); + assertNotNull(committedBlocks); + assertEquals(blockContent.size(), committedBlocks.size()); + } finally { + // Delete + RestAssured.delete("/azure-storage-blob/blob/delete") + .then() + .statusCode(204); + } + } + + @Test + public void appendBlob() { + try { + // Create + RestAssured.given() + .contentType(ContentType.TEXT) + .body(BLOB_CONTENT) + .post("/azure-storage-blob/append/blob/create") + .then() + .statusCode(201); + + // Commit + String appendedContent = BLOB_CONTENT + " Appended"; + RestAssured.given() + .contentType(ContentType.TEXT) + .body(appendedContent) + .post("/azure-storage-blob/append/blob/commit") + .then() + .statusCode(200) + .body(is("true")); + + // Read + RestAssured.get("/azure-storage-blob/blob/read") + .then() + .statusCode(200) + .body(is(appendedContent)); + } finally { + // Delete + RestAssured.delete("/azure-storage-blob/blob/delete") + .then() + .statusCode(204); + } + } + + @Test + public void pageBlob() { + try { + // Create + RestAssured.given() + .post("/azure-storage-blob/page/blob/create") + .then() + .statusCode(201); + + // Upload + RestAssured.given() + .queryParam("pageStart", 0) + .queryParam("pageEnd", 511) + .post("/azure-storage-blob/page/blob/upload") + .then() + .statusCode(200) + .body(is("true")); + + byte[] pageData = RestAssured.get("/azure-storage-blob/blob/read/bytes") + .then() + .statusCode(200) + .extract() + .body() + .asByteArray(); + + assertEquals(512, pageData.length); + + // Get ranges + RestAssured.given() + .queryParam("pageStart", 0) + .queryParam("pageEnd", 511) + .get("/azure-storage-blob/page/blob") + .then() + .statusCode(200) + .body("ranges[0].start", is(0), + "ranges[0].end", is(511)); + + // Resize + RestAssured.given() + .queryParam("pageStart", 0) + .queryParam("pageEnd", 1023) + .post("/azure-storage-blob/page/blob/resize") + .then() + .statusCode(200) + .body(is("true")); + + // Read after resize + pageData = RestAssured.get("/azure-storage-blob/blob/read/bytes") + .then() + .statusCode(200) + .extract() + .body() + .asByteArray(); + + assertEquals(1024, pageData.length); + + // Verify page data beyond the resized point is empty + for (int i = 512; i < pageData.length; i++) { + if (pageData[i] != 0) { + fail("Expected byte element at position " + i + " to be zero value"); + } + } + + // Clear + RestAssured.given() + .queryParam("pageStart", 0) + .queryParam("pageEnd", 1023) + .post("/azure-storage-blob/page/blob/clear") + .then() + .statusCode(200) + .body(is("true")); + + // Read after clear + pageData = RestAssured.get("/azure-storage-blob/blob/read/bytes") + .then() + .statusCode(200) + .extract() + .body() + .asByteArray(); + + // Verify all page data is empty + for (int i = 0; i < pageData.length; i++) { + if (pageData[i] != 0) { + fail("Expected byte element at position " + i + " to be zero value"); + } + } + } finally { + // Delete + RestAssured.delete("/azure-storage-blob/blob/delete") + .then() + .statusCode(204); + } } + @Test + public void blobContainer() { + String alternativeContainerName = "cq-test-" + UUID.randomUUID(); + + try { + // Create + RestAssured.given() + .queryParam("containerName", alternativeContainerName) + .post("/azure-storage-blob/blob/container") + .then() + .statusCode(201); + + // List + String containerName = ConfigProvider.getConfig().getValue("azure.blob.container.name", String.class); + RestAssured.get("/azure-storage-blob/blob/container") + .then() + .statusCode(200) + .body("containers.name", + containsInAnyOrder(containerName, alternativeContainerName)); + } finally { + // Delete + RestAssured.given() + .queryParam("containerName", alternativeContainerName) + .delete("/azure-storage-blob/blob/container") + .then() + .statusCode(204); + } + } + + @Test + public void copyBlob() { + String alternativeContainerName = "cq-test-" + UUID.randomUUID(); + + try { + // Create container to copy to + RestAssured.given() + .queryParam("containerName", alternativeContainerName) + .post("/azure-storage-blob/blob/container") + .then() + .statusCode(201); + + // List + String containerName = ConfigProvider.getConfig().getValue("azure.blob.container.name", String.class); + RestAssured.get("/azure-storage-blob/blob/container") + .then() + .statusCode(200) + .body("containers.name", + containsInAnyOrder(containerName, alternativeContainerName)); + + // Create blob in first container + RestAssured.given() + .contentType(ContentType.TEXT) + .body(BLOB_CONTENT) + .post("/azure-storage-blob/blob/create") + .then() + .statusCode(201); + + // Read + RestAssured.get("/azure-storage-blob/blob/read") + .then() + .statusCode(200) + .body(is(BLOB_CONTENT)); + + // Copy blob to alternate storage container + RestAssured.given() + .queryParam("containerName", alternativeContainerName) + .post("/azure-storage-blob/blob/copy") + .then() + .statusCode(200); + + // Read blob from alternate storage container + RestAssured.given() + .queryParam("containerName", alternativeContainerName) + .get("/azure-storage-blob/blob/read") + .then() + .statusCode(200) + .body(is(BLOB_CONTENT)); + } finally { + // Delete + RestAssured.given() + .queryParam("containerName", alternativeContainerName) + .delete("/azure-storage-blob/blob/container") + .then() + .statusCode(204); + + RestAssured.delete("/azure-storage-blob/blob/delete") + .then() + .statusCode(204); + } + } + + @Test + public void blobConsumer() { + try { + // Start blob consumer + RestAssured.given() + .post("/azure-storage-blob/consumer/true") + .then() + .statusCode(204); + + // Create blob + RestAssured.given() + .contentType(ContentType.TEXT) + .body(BLOB_CONTENT) + .post("/azure-storage-blob/blob/create") + .then() + .statusCode(201); + + // Fetch results + RestAssured.get("/azure-storage-blob/consumed/blobs") + .then() + .statusCode(200) + .body(is(BLOB_CONTENT)); + } finally { + // Stop blob consumer + RestAssured.given() + .post("/azure-storage-blob/consumer/false") + .then() + .statusCode(204); + } + } + + // Change feed is not available in Azurite + @EnabledIf({ MockBackendDisabled.class }) + @Test + public void changeFeed() { + try { + String eTag = RestAssured.given() + .contentType(ContentType.TEXT) + .body(BLOB_CONTENT) + .post("/azure-storage-blob/blob/create") + .then() + .statusCode(201) + .extract() + .body() + .asString(); + + OffsetDateTime now = OffsetDateTime.now(); + OffsetDateTime startTime = now.minus(5, ChronoUnit.MINUTES); + OffsetDateTime endTime = now.plus(5, ChronoUnit.MINUTES); + + // Poll change feed until the blob just created is present + Awaitility.await().pollInterval(5, TimeUnit.SECONDS).timeout(5, TimeUnit.MINUTES).until(() -> RestAssured.given() + .queryParam("startTime", startTime.toString()) + .queryParam("endTime", endTime.toString()) + .queryParam("etag", eTag) + .get("/azure-storage-blob/changes") + .then() + .statusCode(200) + .extract() + .body() + .asString() + .equals("true")); + } finally { + RestAssured.delete("/azure-storage-blob/blob/delete") + .then() + .statusCode(204); + } + } } diff --git a/integration-tests-support/mock-backend/src/main/java/org/apache/camel/quarkus/test/mock/backend/MockBackendDisabled.java b/integration-tests-support/mock-backend/src/main/java/org/apache/camel/quarkus/test/mock/backend/MockBackendDisabled.java new file mode 100644 index 0000000..40d88fe --- /dev/null +++ b/integration-tests-support/mock-backend/src/main/java/org/apache/camel/quarkus/test/mock/backend/MockBackendDisabled.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.quarkus.test.mock.backend; + +import java.util.function.BooleanSupplier; + +/** + * {@link BooleanSupplier} for use with org.apache.camel.quarkus.test.EnabledIf to enable tests if the mock back end is + * disabled. + */ +public class MockBackendDisabled implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return !MockBackendUtils.startMockBackend(false); + } +} diff --git a/integration-tests/azure-grouped/pom.xml b/integration-tests/azure-grouped/pom.xml index f42d214..ca58d99 100644 --- a/integration-tests/azure-grouped/pom.xml +++ b/integration-tests/azure-grouped/pom.xml @@ -42,7 +42,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-jackson</artifactId> + <artifactId>quarkus-resteasy-jsonb</artifactId> </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> @@ -58,9 +58,17 @@ </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-mock</artifactId> </dependency> <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-seda</artifactId> + </dependency> + <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> @@ -72,6 +80,11 @@ </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-integration-test-support</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-integration-tests-support-azure</artifactId> <scope>test</scope> </dependency> @@ -216,6 +229,19 @@ </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-mock-deployment</artifactId> <version>${project.version}</version> <type>pom</type> @@ -227,6 +253,19 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-seda-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> </dependencies> </profile> <profile>
