This is an automated email from the ASF dual-hosted git repository. jamesnetherton 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 85197d3afa8 Upgrade Google Cloud libraries-bom to 26.21.0 85197d3afa8 is described below commit 85197d3afa8b5bb4eb5b02f6cd9e1200cc9b9b09 Author: James Netherton <jamesnether...@gmail.com> AuthorDate: Thu Aug 3 11:59:22 2023 +0100 Upgrade Google Cloud libraries-bom to 26.21.0 --- .../camel-google/camel-google-storage/pom.xml | 7 +- .../storage/localstorage/FakeStorageRpc.java | 247 +++++++++++++++++++-- parent/pom.xml | 2 +- 3 files changed, 234 insertions(+), 22 deletions(-) diff --git a/components/camel-google/camel-google-storage/pom.xml b/components/camel-google/camel-google-storage/pom.xml index 28e7b484e2c..fd42e61da95 100644 --- a/components/camel-google/camel-google-storage/pom.xml +++ b/components/camel-google/camel-google-storage/pom.xml @@ -77,6 +77,11 @@ <version>${mockito-version}</version> <scope>test</scope> </dependency> - + <dependency> + <groupId>com.google.cloud</groupId> + <artifactId>google-cloud-core</artifactId> + <scope>test</scope> + <classifier>tests</classifier> + </dependency> </dependencies> </project> diff --git a/components/camel-google/camel-google-storage/src/test/java/org/apache/camel/component/google/storage/localstorage/FakeStorageRpc.java b/components/camel-google/camel-google-storage/src/test/java/org/apache/camel/component/google/storage/localstorage/FakeStorageRpc.java index 2132dcda49e..ce8b6789815 100644 --- a/components/camel-google/camel-google-storage/src/test/java/org/apache/camel/component/google/storage/localstorage/FakeStorageRpc.java +++ b/components/camel-google/camel-google-storage/src/test/java/org/apache/camel/component/google/storage/localstorage/FakeStorageRpc.java @@ -16,10 +16,16 @@ */ package org.apache.camel.component.google.storage.localstorage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.math.BigInteger; +import java.net.URLDecoder; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -29,8 +35,20 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; - +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.LowLevelHttpRequest; +import com.google.api.client.http.LowLevelHttpResponse; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.client.testing.http.MockHttpTransport; +import com.google.api.client.testing.http.MockLowLevelHttpRequest; +import com.google.api.client.testing.http.MockLowLevelHttpResponse; import com.google.api.client.util.DateTime; +import com.google.api.client.util.Preconditions; import com.google.api.services.storage.model.Bucket; import com.google.api.services.storage.model.ServiceAccount; import com.google.api.services.storage.model.StorageObject; @@ -82,6 +100,11 @@ class FakeStorageRpc extends StorageRpcTestBase { private static final SimpleDateFormat RFC_3339_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + private static final int OK = 200; + private static final int PARTIAL_CONTENT = 206; + private static final int NOT_FOUND = 404; + private static final byte[] EMPTY_BYTES = new byte[0]; + // fullname -> metadata Map<String, StorageObject> metadata = new HashMap<>(); // fullname -> contents @@ -303,7 +326,7 @@ class FakeStorageRpc extends StorageRpcTestBase { public byte[] load(StorageObject storageObject, Map<Option, ?> options) throws StorageException { String key = fullname(storageObject); if (!contents.containsKey(key)) { - throw new StorageException(404, "File not found: " + key); + throw new StorageException(NOT_FOUND, "File not found: " + key); } return contents.get(key); } @@ -314,17 +337,16 @@ class FakeStorageRpc extends StorageRpcTestBase { throws StorageException { // if non-null, then we check the file's at that generation. Long generationMatch = null; - for (Map.Entry<Option, ?> entry : options.entrySet()) { - Option op = entry.getKey(); + for (Option op : options.keySet()) { if (op.equals(StorageRpc.Option.IF_GENERATION_MATCH)) { - generationMatch = (Long) entry.getValue(); + generationMatch = (Long) options.get(op); } else { throw new UnsupportedOperationException("Unknown option: " + op); } } String key = fullname(from); if (!contents.containsKey(key)) { - throw new StorageException(404, "File not found: " + key); + throw new StorageException(NOT_FOUND, "File not found: " + key); } checkGeneration(key, generationMatch); long position = zposition; @@ -350,17 +372,16 @@ class FakeStorageRpc extends StorageRpcTestBase { StorageObject from, Map<Option, ?> options, long position, OutputStream outputStream) { // if non-null, then we check the file's at that generation. Long generationMatch = null; - for (Map.Entry<Option, ?> entry : options.entrySet()) { - Option op = entry.getKey(); + for (Option op : options.keySet()) { if (op.equals(StorageRpc.Option.IF_GENERATION_MATCH)) { - generationMatch = (Long) entry.getValue(); + generationMatch = (Long) options.get(op); } else { throw new UnsupportedOperationException("Unknown option: " + op); } } String key = fullname(from); if (!contents.containsKey(key)) { - throw new StorageException(404, "File not found: " + key); + throw new StorageException(NOT_FOUND, "File not found: " + key); } checkGeneration(key, generationMatch); if (position < 0) { @@ -385,10 +406,10 @@ class FakeStorageRpc extends StorageRpcTestBase { String key = fullname(object); // if non-null, then we check the file's at that generation. Long generationMatch = null; - for (Map.Entry<Option, ?> entry : options.entrySet()) { + for (Option option : options.keySet()) { // this is a bit of a hack, since we don't implement generations. - if (entry.getKey() == Option.IF_GENERATION_MATCH) { - generationMatch = (Long) entry.getValue(); + if (option == Option.IF_GENERATION_MATCH) { + generationMatch = (Long) options.get(option); } } checkGeneration(key, generationMatch); @@ -457,15 +478,15 @@ class FakeStorageRpc extends StorageRpcTestBase { // a little hackish, just good enough for the tests to work. if (!contents.containsKey(sourceKey)) { - throw new StorageException(404, "File not found: " + sourceKey); + throw new StorageException(NOT_FOUND, "File not found: " + sourceKey); } // if non-null, then we check the file's at that generation. Long generationMatch = null; - for (Map.Entry<Option, ?> entry : rewriteRequest.targetOptions.entrySet()) { + for (Option option : rewriteRequest.targetOptions.keySet()) { // this is a bit of a hack, since we don't implement generations. - if (entry.getKey() == Option.IF_GENERATION_MATCH) { - generationMatch = (Long) entry.getValue(); + if (option == Option.IF_GENERATION_MATCH) { + generationMatch = (Long) rewriteRequest.targetOptions.get(option); } } @@ -474,7 +495,10 @@ class FakeStorageRpc extends StorageRpcTestBase { // if this is a new file, set generation to 1, else increment the existing generation long generation = 1; if (metadata.containsKey(destKey)) { - generation = metadata.get(destKey).getGeneration() + 1; + Long storedGeneration = metadata.get(destKey).getGeneration(); + if (null != storedGeneration) { + generation = storedGeneration + 1; + } } checkGeneration(destKey, generationMatch); @@ -502,7 +526,7 @@ class FakeStorageRpc extends StorageRpcTestBase { } private String fullname(StorageObject so) { - return so.getBucket() + "/" + so.getName(); + return fullname(so.getBucket(), so.getName()); } private BigInteger size(StorageObject so) { @@ -539,7 +563,8 @@ class FakeStorageRpc extends StorageRpcTestBase { Long generation = metadata.get(key).getGeneration(); if (!generationMatch.equals(generation)) { throw new StorageException( - 404, "Generation mismatch. Requested " + generationMatch + " but got " + generation); + NOT_FOUND, + "Generation mismatch. Requested " + generationMatch + " but got " + generation); } } } @@ -575,4 +600,186 @@ class FakeStorageRpc extends StorageRpcTestBase { public ServiceAccount getServiceAccount(String projectId) { return null; } + + @Override + public com.google.api.services.storage.Storage getStorage() { + HttpTransport transport = new FakeStorageRpcHttpTransport(); + HttpRequestInitializer httpRequestInitializer = request -> { + }; + return new com.google.api.services.storage.Storage( + transport, new GsonFactory(), httpRequestInitializer); + } + + private static String fullname(String bucket, String object) { + return String.format("http://localhost:65555/b/%s/o/%s", bucket, object); + } + + private static final String KEY_PATTERN_DEFINITION = "^.*?/b/(.*?)/o/(.*?)(?:[?].*|$)"; + private static final Pattern KEY_PATTERN = Pattern.compile(KEY_PATTERN_DEFINITION); + + MyMockLowLevelHttpRequest create(String method, String url) { + Matcher m = KEY_PATTERN.matcher(url); + Preconditions.checkArgument( + m.matches(), + "Provided url '%s' does not match expected pattern '%s'", + url, + KEY_PATTERN_DEFINITION); + + String bucket = m.group(1); + String object = m.group(2); + + String decode = urlDecode(object); + String key = fullname(bucket, decode); + return new MyMockLowLevelHttpRequest(method, url, key); + } + + private static String urlDecode(String object) { + try { + return URLDecoder.decode(object, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private class MyMockLowLevelHttpRequest extends MockLowLevelHttpRequest { + + private final String method; + private final String key; + + private MyMockLowLevelHttpRequest(String method, String url, String key) { + super(url); + this.method = method; + this.key = key; + } + + /** + * {@link MockLowLevelHttpRequest#execute} tries to return a value, but we need to compute based upon possible + * request mutations. So override it to call {@link #getResponse()}. + */ + @Override + public LowLevelHttpResponse execute() throws IOException { + return getResponse(); + } + + @Override + public MockLowLevelHttpResponse getResponse() { + + MockLowLevelHttpResponse resp = new MockLowLevelHttpResponse(); + byte[] bytes = contents.get(key); + StorageObject storageObject = metadata.get(key); + if (bytes == null && storageObject == null) { + resp.setStatusCode(NOT_FOUND); + } else if (storageObject != null && method.equals("PUT")) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + this.getStreamingContent().writeTo(baos); + byte[] byteArray = baos.toByteArray(); + if (storageObject.getGeneration() != null) { + contents.put(key, concat(bytes, byteArray)); + } else { + contents.put(key, byteArray); + } + + List<String> contentRanges = getHeaders().get("content-range"); + if (contentRanges != null && !contentRanges.isEmpty()) { + String contentRange = contentRanges.get(0); + // finalization PUT + // Pattern matches both froms of begin-end/size, */size + Pattern pattern = Pattern.compile("bytes (?:\\d+-\\d+|\\*)/(\\d+)"); + Matcher matcher = pattern.matcher(contentRange); + if (matcher.matches()) { + storageObject.setSize(new BigInteger(matcher.group(1), 10)); + storageObject.setGeneration(System.currentTimeMillis()); + DateTime now = now(); + storageObject.setTimeCreated(now); + storageObject.setUpdated(now); + String string = GsonFactory.getDefaultInstance().toPrettyString(storageObject); + resp.addHeader("Content-Type", "application/json;charset=utf-8"); + resp.addHeader("Content-Length", String.valueOf(string.length())); + resp.setContent(string); + } else { + resp.setStatusCode(NOT_FOUND); + } + } else { + resp.setStatusCode(NOT_FOUND); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + int length = bytes.length; + Map<String, List<String>> headers = getHeaders(); + List<String> range = headers.get("range"); + int begin = 0; + int endInclusive = length - 1; + if (range != null && !range.isEmpty()) { + String rangeString = range.get(0).substring("range=".length()); + if ("0-".equals(rangeString)) { + resp.setStatusCode(OK); + } else if (rangeString.startsWith("-")) { + // we don't support negative offsets yet + resp.setStatusCode(400); + return resp; + } else if (rangeString.endsWith("-")) { + // only lower bounded + String beginS = rangeString.substring(0, rangeString.length() - 1); + begin = Integer.parseInt(beginS); + resp.setStatusCode(PARTIAL_CONTENT); + } else { + // otherwise a lower and upper bound + int i = rangeString.indexOf('-'); + if (i == -1) { + resp.setStatusCode(400); + return resp; + } else { + String beginS = rangeString.substring(0, i); + String endInclusiveS = rangeString.substring(i + 1); + begin = Integer.parseInt(beginS); + endInclusive = Integer.parseInt(endInclusiveS); + resp.setStatusCode(PARTIAL_CONTENT); + } + } + + if (begin > length) { + resp.addHeader("Content-Range", String.format("bytes */%d", length)); + resp.setContent(EMPTY_BYTES); + resp.setContent(new ByteArrayInputStream(EMPTY_BYTES)); + } else { + int newLength = endInclusive - begin + 1; + resp.addHeader("Content-Length", String.valueOf(newLength)); + // Content-Range: bytes 4-9/512 + resp.addHeader( + "Content-Range", String.format("bytes %d-%d/%d", begin, endInclusive, length)); + byte[] content = Arrays.copyOfRange(bytes, begin, endInclusive + 1); + resp.setContent(content); + resp.setContent(new ByteArrayInputStream(content)); + } + } else { + resp.addHeader("Content-Length", String.valueOf(length)); + resp.setContent(bytes); + resp.setContent(new ByteArrayInputStream(bytes)); + resp.setStatusCode(OK); + } + } + return resp; + } + } + + private static byte[] concat(byte[]... arrays) { + int total = Arrays.stream(arrays).filter(Objects::nonNull).mapToInt(a -> a.length).sum(); + byte[] retVal = new byte[total]; + + ByteBuffer wrap = ByteBuffer.wrap(retVal); + Arrays.stream(arrays).filter(Objects::nonNull).forEach(wrap::put); + + return retVal; + } + + private class FakeStorageRpcHttpTransport extends MockHttpTransport { + + @Override + public LowLevelHttpRequest buildRequest(String method, String url) { + return create(method, url); + } + } } diff --git a/parent/pom.xml b/parent/pom.xml index 4c077c52fba..69a7c424cc1 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -176,7 +176,7 @@ <google-api-services-sheets-version>v4-rev20210629-1.32.1</google-api-services-sheets-version> <google-api-services-mail-version>v1-rev20220404-1.32.1</google-api-services-mail-version> <google-oauth-client-version>1.34.1</google-oauth-client-version> - <google-cloud-bom-version>26.19.0</google-cloud-bom-version> + <google-cloud-bom-version>26.21.0</google-cloud-bom-version> <google-cloud-functions-bom-version>2.23.0</google-cloud-functions-bom-version> <google-cloud-secretmanager-bom-version>2.21.0</google-cloud-secretmanager-bom-version> <graaljs-version>23.0.1</graaljs-version>