This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 9231ea6 CAMEL-17392 (#6598) 9231ea6 is described below commit 9231ea60e5d91c3edeabbfea49dd6062d9adf665 Author: aevangelista81 <96737327+aevangelist...@users.noreply.github.com> AuthorDate: Wed Dec 29 15:08:35 2021 +0100 CAMEL-17392 (#6598) Camel-google-storage - Provide a way to filter blobs in a bucket Co-authored-by: Andrea Evangelista <and...@graphaware.com> --- .../GoogleCloudStorageComponentConfigurer.java | 3 + .../GoogleCloudStorageEndpointConfigurer.java | 3 + .../GoogleCloudStorageEndpointUriFactory.java | 3 +- .../component/google/storage/google-storage.json | 2 + .../storage/GoogleCloudStorageConfiguration.java | 14 +++ .../google/storage/GoogleCloudStorageConsumer.java | 11 +- .../storage/unit/ConsumerWithFilterOptionTest.java | 122 +++++++++++++++++++++ 7 files changed, 156 insertions(+), 2 deletions(-) diff --git a/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageComponentConfigurer.java b/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageComponentConfigurer.java index 6ddc88f..86e582f 100644 --- a/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageComponentConfigurer.java +++ b/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageComponentConfigurer.java @@ -41,6 +41,7 @@ public class GoogleCloudStorageComponentConfigurer extends PropertyConfigurerSup case "destinationBucket": getOrCreateConfiguration(target).setDestinationBucket(property(camelContext, java.lang.String.class, value)); return true; case "downloadfilename": case "downloadFileName": getOrCreateConfiguration(target).setDownloadFileName(property(camelContext, java.lang.String.class, value)); return true; + case "filter": getOrCreateConfiguration(target).setFilter(property(camelContext, java.lang.String.class, value)); return true; case "includebody": case "includeBody": getOrCreateConfiguration(target).setIncludeBody(property(camelContext, boolean.class, value)); return true; case "includefolders": @@ -85,6 +86,7 @@ public class GoogleCloudStorageComponentConfigurer extends PropertyConfigurerSup case "destinationBucket": return java.lang.String.class; case "downloadfilename": case "downloadFileName": return java.lang.String.class; + case "filter": return java.lang.String.class; case "includebody": case "includeBody": return boolean.class; case "includefolders": @@ -125,6 +127,7 @@ public class GoogleCloudStorageComponentConfigurer extends PropertyConfigurerSup case "destinationBucket": return getOrCreateConfiguration(target).getDestinationBucket(); case "downloadfilename": case "downloadFileName": return getOrCreateConfiguration(target).getDownloadFileName(); + case "filter": return getOrCreateConfiguration(target).getFilter(); case "includebody": case "includeBody": return getOrCreateConfiguration(target).isIncludeBody(); case "includefolders": diff --git a/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointConfigurer.java b/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointConfigurer.java index 00d8366..a9ab367 100644 --- a/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointConfigurer.java +++ b/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointConfigurer.java @@ -42,6 +42,7 @@ public class GoogleCloudStorageEndpointConfigurer extends PropertyConfigurerSupp case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true; case "exchangepattern": case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true; + case "filter": target.getConfiguration().setFilter(property(camelContext, java.lang.String.class, value)); return true; case "greedy": target.setGreedy(property(camelContext, boolean.class, value)); return true; case "includebody": case "includeBody": target.getConfiguration().setIncludeBody(property(camelContext, boolean.class, value)); return true; @@ -116,6 +117,7 @@ public class GoogleCloudStorageEndpointConfigurer extends PropertyConfigurerSupp case "exceptionHandler": return org.apache.camel.spi.ExceptionHandler.class; case "exchangepattern": case "exchangePattern": return org.apache.camel.ExchangePattern.class; + case "filter": return java.lang.String.class; case "greedy": return boolean.class; case "includebody": case "includeBody": return boolean.class; @@ -186,6 +188,7 @@ public class GoogleCloudStorageEndpointConfigurer extends PropertyConfigurerSupp case "exceptionHandler": return target.getExceptionHandler(); case "exchangepattern": case "exchangePattern": return target.getExchangePattern(); + case "filter": return target.getConfiguration().getFilter(); case "greedy": return target.isGreedy(); case "includebody": case "includeBody": return target.getConfiguration().isIncludeBody(); diff --git a/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointUriFactory.java b/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointUriFactory.java index a7cebe6..a5de686 100644 --- a/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointUriFactory.java +++ b/components/camel-google/camel-google-storage/src/generated/java/org/apache/camel/component/google/storage/GoogleCloudStorageEndpointUriFactory.java @@ -20,7 +20,7 @@ public class GoogleCloudStorageEndpointUriFactory extends org.apache.camel.suppo private static final Set<String> PROPERTY_NAMES; private static final Set<String> SECRET_PROPERTY_NAMES; static { - Set<String> props = new HashSet<>(34); + Set<String> props = new HashSet<>(35); props.add("backoffMultiplier"); props.add("bucketName"); props.add("destinationBucket"); @@ -46,6 +46,7 @@ public class GoogleCloudStorageEndpointUriFactory extends org.apache.camel.suppo props.add("includeFolders"); props.add("storageClient"); props.add("backoffIdleThreshold"); + props.add("filter"); props.add("lazyStartProducer"); props.add("delay"); props.add("includeBody"); diff --git a/components/camel-google/camel-google-storage/src/generated/resources/org/apache/camel/component/google/storage/google-storage.json b/components/camel-google/camel-google-storage/src/generated/resources/org/apache/camel/component/google/storage/google-storage.json index 092da32..ac97787 100644 --- a/components/camel-google/camel-google-storage/src/generated/resources/org/apache/camel/component/google/storage/google-storage.json +++ b/components/camel-google/camel-google-storage/src/generated/resources/org/apache/camel/component/google/storage/google-storage.json @@ -32,6 +32,7 @@ "deleteAfterRead": { "kind": "property", "displayName": "Delete After Read", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "Delete objects from the bucket after they have been retrieved. The delete [...] "destinationBucket": { "kind": "property", "displayName": "Destination Bucket", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "Define the destination bucket where an object must be moved when moveAfterRead is set [...] "downloadFileName": { "kind": "property", "displayName": "Download File Name", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "The folder or filename to use when downloading the blob. By default, this specifies th [...] + "filter": { "kind": "property", "displayName": "Filter", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "A regular expression to include only blobs with name matching it." }, "includeBody": { "kind": "property", "displayName": "Include Body", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "If it is true, the Object exchange will be consumed and put into the body. If false [...] "includeFolders": { "kind": "property", "displayName": "Include Folders", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "If it is true, the folders\/directories will be consumed. If it is false, the [...] "moveAfterRead": { "kind": "property", "displayName": "Move After Read", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "Move objects from the origin bucket to a different bucket after they have bee [...] @@ -51,6 +52,7 @@ "deleteAfterRead": { "kind": "parameter", "displayName": "Delete After Read", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "Delete objects from the bucket after they have been retrieved. The delete [...] "destinationBucket": { "kind": "parameter", "displayName": "Destination Bucket", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "Define the destination bucket where an object must be moved when moveAfterRead is se [...] "downloadFileName": { "kind": "parameter", "displayName": "Download File Name", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "The folder or filename to use when downloading the blob. By default, this specifies t [...] + "filter": { "kind": "parameter", "displayName": "Filter", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "A regular expression to include only blobs with name matching it." }, "includeBody": { "kind": "parameter", "displayName": "Include Body", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "If it is true, the Object exchange will be consumed and put into the body. If fals [...] "includeFolders": { "kind": "parameter", "displayName": "Include Folders", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "If it is true, the folders\/directories will be consumed. If it is false, th [...] "moveAfterRead": { "kind": "parameter", "displayName": "Move After Read", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.google.storage.GoogleCloudStorageConfiguration", "configurationField": "configuration", "description": "Move objects from the origin bucket to a different bucket after they have be [...] diff --git a/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConfiguration.java b/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConfiguration.java index cfbf2bf..7e98015 100644 --- a/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConfiguration.java +++ b/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConfiguration.java @@ -76,6 +76,9 @@ public class GoogleCloudStorageConfiguration implements Cloneable { @Metadata(autowired = true) private Storage storageClient; + @UriParam(label = "consumer", description = "A regular expression to include only blobs with name matching it.") + private String filter; + public String getBucketName() { return this.bucketName; } @@ -260,4 +263,15 @@ public class GoogleCloudStorageConfiguration implements Cloneable { } } + /** + * A regular expression to include only blobs with name matching it. + */ + public void setFilter(String filter) { + this.filter = filter; + } + + public String getFilter() { + return filter; + } + } diff --git a/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConsumer.java b/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConsumer.java index 39a9b9f..5f37612 100644 --- a/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConsumer.java +++ b/components/camel-google/camel-google-storage/src/main/java/org/apache/camel/component/google/storage/GoogleCloudStorageConsumer.java @@ -87,6 +87,7 @@ public class GoogleCloudStorageConsumer extends ScheduledBatchPollingConsumer { String fileName = getConfiguration().getObjectName(); String bucketName = getConfiguration().getBucketName(); Queue<Exchange> exchanges; + String filter = getConfiguration().getFilter(); if (fileName != null) { LOG.trace("Getting object in bucket [{}] with file name [{}]...", bucketName, fileName); @@ -99,7 +100,15 @@ public class GoogleCloudStorageConsumer extends ScheduledBatchPollingConsumer { List<Blob> bloblist = new LinkedList<>(); for (Blob blob : getStorageClient().list(bucketName).iterateAll()) { - bloblist.add(blob); + + if (filter != null && !filter.isEmpty()) { + if (blob.getBlobId().getName().matches(filter)) { + bloblist.add(blob); + } + } else { + bloblist.add(blob); + } + } if (LOG.isTraceEnabled()) { diff --git a/components/camel-google/camel-google-storage/src/test/java/org/apache/camel/component/google/storage/unit/ConsumerWithFilterOptionTest.java b/components/camel-google/camel-google-storage/src/test/java/org/apache/camel/component/google/storage/unit/ConsumerWithFilterOptionTest.java new file mode 100644 index 0000000..46139bc --- /dev/null +++ b/components/camel-google/camel-google-storage/src/test/java/org/apache/camel/component/google/storage/unit/ConsumerWithFilterOptionTest.java @@ -0,0 +1,122 @@ +/* + * 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.component.google.storage.unit; + +import java.nio.file.Path; + +import org.apache.camel.EndpointInject; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.google.storage.GoogleCloudStorageConstants; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class ConsumerWithFilterOptionTest extends GoogleCloudStorageBaseTest { + + @EndpointInject + private ProducerTemplate template; + + @EndpointInject("mock:result") + private MockEndpoint result; + + @EndpointInject("mock:consumedObjects") + private MockEndpoint consumedObjects; + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + + String endpoint = "google-storage://myCamelBucket?autoCreateBucket=true"; + + from("direct:putObject") + .startupOrder(1) + .to(endpoint) + .to("mock:result"); + + from("google-storage://myCamelBucket?" + + "moveAfterRead=true" + + "&destinationBucket=camelDestinationBucket" + + "&autoCreateBucket=true" + + "&deleteAfterRead=true" + + "&includeBody=true" + + "&filter=.*.csv") + .startupOrder(2) + .to("mock:consumedObjects"); + + } + }; + } + + @Test + public void onlyCsvFilesShouldBeFiltered(@TempDir Path tempDir) throws Exception { + + final int totalNumberOfFiles = 5; + final int numberOfFilteredFiles = 2; + + Path path = tempDir.resolve("file"); + + uploadFiles(path.toString(), "csv", 2); + uploadFiles(path.toString(), "txt", 3); + + result.expectedMessageCount(totalNumberOfFiles); + consumedObjects.expectedMessageCount(numberOfFilteredFiles); + + assertMockEndpointsSatisfied(); + + context.stop(); + + } + + @Test + public void noFilesShouldBeFitered(@TempDir Path tempDir) throws Exception { + + final int totalNumberOfFiles = 5; + final int numberOfFilteredFiles = 0; + + Path path = tempDir.resolve("file"); + + uploadFiles(path.toString(), "json", 2); + uploadFiles(path.toString(), "txt", 3); + + result.expectedMessageCount(totalNumberOfFiles); + consumedObjects.expectedMessageCount(numberOfFilteredFiles); + + assertMockEndpointsSatisfied(); + + context.stop(); + + } + + private void uploadFiles(final String fileNamePrefix, final String extension, final int numberOfFiles) { + + for (int i = 0; i < numberOfFiles; i++) { + + final String filename = String.format("%s_%s.%s", fileNamePrefix, i, extension); + final String body = String.format("body_%s", i); + //upload a file + + template.send("direct:putObject", exchange -> { + exchange.getIn().setHeader(GoogleCloudStorageConstants.OBJECT_NAME, filename); + exchange.getIn().setBody(body); + }); + } + } + +}