From bced69ebcd949a67465697d5817fe6d033305d9c Mon Sep 17 00:00:00 2001 From: Todd Hill Date: Wed, 17 Apr 2024 16:28:01 -0400 Subject: [PATCH] Add examples to show tracking of an upload and a download https://issues.amazon.com/issues/240b0410-5802-4b05-8467-35bf4dc16537 --- .doc_gen/metadata/s3_metadata.yaml | 22 +++++- javav2/example_code/s3/README.md | 13 ++++ .../main/java/com/example/s3/ParseUri.java | 2 +- .../s3/lockscenario/S3ObjectLockWorkflow.java | 4 +- .../s3/transfermanager/DownloadFile.java | 76 ++++++++++++++---- .../s3/transfermanager/S3ClientFactory.java | 4 +- .../s3/transfermanager/UploadFile.java | 59 ++++++++++++-- .../example/s3/util/MemoryLog4jAppender.java | 5 ++ .../s3/src/main/resources/log4j2.xml | 23 ++++-- .../s3/src/test/java/TransferManagerTest.java | 78 ++++++++++++++----- .../java/com/example/s3/ParseUriTest.java | 43 ++++++++++ 11 files changed, 271 insertions(+), 58 deletions(-) create mode 100644 javav2/example_code/s3/src/test/java/com/example/s3/ParseUriTest.java diff --git a/.doc_gen/metadata/s3_metadata.yaml b/.doc_gen/metadata/s3_metadata.yaml index c270dee69dd..dffe557d4e5 100644 --- a/.doc_gen/metadata/s3_metadata.yaml +++ b/.doc_gen/metadata/s3_metadata.yaml @@ -2997,7 +2997,7 @@ s3_Scenario_UploadStream: services: s3: {} s3_Scenario_MultipartUpload: - title: Perform a multipart upload to an &S3; object using an &AWS; SDK + title: Perform a multipart upload of an &S3; object using an &AWS; SDK title_abbrev: Perform a multipart upload synopsis: perform a multipart upload to an &S3; object. category: Scenarios @@ -3172,3 +3172,23 @@ s3_SelectObjectContent: - s3.java2.async.selectObjectContentMethod.main services: s3: {SelectObjectContent} +s3_Scenario_TrackUploadDownload: + title: Track an &S3; object upload or download using an &AWS; SDK + title_abbrev: Track uploads and downloads + synopsis: track an &S3; object upload or download. + category: Scenarios + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/s3 + sdkguide: + excerpts: + - description: Track the progress of a file upload. + snippet_tags: + - s3.tm.java2.trackuploadfile.main + - description: Track the progress of a file download. + snippet_tags: + - s3.tm.java2.trackdownloadfile.main + services: + s3: {PutObject, GetObject} diff --git a/javav2/example_code/s3/README.md b/javav2/example_code/s3/README.md index c350f241221..290748cffaa 100644 --- a/javav2/example_code/s3/README.md +++ b/javav2/example_code/s3/README.md @@ -78,6 +78,7 @@ functions within the same service. - [Lock Amazon S3 objects](src/main/java/com/example/s3/lockscenario/S3ObjectLockWorkflow.java) - [Parse URIs](src/main/java/com/example/s3/ParseUri.java) - [Perform a multipart upload](src/main/java/com/example/s3/PerformMultiPartUpload.java) +- [Track uploads and downloads](src/main/java/com/example/s3/transfermanager/UploadFile.java) - [Upload or download large files](src/main/java/com/example/s3/transfermanager/DownloadToDirectory.java) - [Upload stream of unknown size](src/main/java/com/example/s3/async/PutObjectFromStreamAsync.java) - [Use checksums](src/main/java/com/example/s3/BasicOpsWithChecksums.java) @@ -155,6 +156,18 @@ This example shows you how to perform a multipart upload to an Amazon S3 object. +#### Track uploads and downloads + +This example shows you how to track an Amazon S3 object upload or download. + + + + + + + + + #### Upload or download large files This example shows you how to upload or download large files to and from Amazon S3. diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/ParseUri.java b/javav2/example_code/s3/src/main/java/com/example/s3/ParseUri.java index ae2f5a39333..1b7fe0d24d8 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/ParseUri.java +++ b/javav2/example_code/s3/src/main/java/com/example/s3/ParseUri.java @@ -113,7 +113,7 @@ private static void log(String s3UriElement, Object element) { if (element == null) { logger.info("{}: {}", s3UriElement, "null"); } else { - logger.info("{}: {}", s3UriElement, element.toString()); + logger.info("{}: {}", s3UriElement, element); } } // snippet-end:[s3.java2.scenario_uriparsing.main] diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/lockscenario/S3ObjectLockWorkflow.java b/javav2/example_code/s3/src/main/java/com/example/s3/lockscenario/S3ObjectLockWorkflow.java index 00a7dc9793d..de2301e184a 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/lockscenario/S3ObjectLockWorkflow.java +++ b/javav2/example_code/s3/src/main/java/com/example/s3/lockscenario/S3ObjectLockWorkflow.java @@ -31,11 +31,11 @@ */ public class S3ObjectLockWorkflow { + public static final String DASHES = new String(new char[80]).replace("\0", "-"); static String bucketName; + static S3LockActions s3LockActions; private static final List bucketNames = new ArrayList<>(); private static final List fileNames = new ArrayList<>(); - public static final String DASHES = new String(new char[80]).replace("\0", "-"); - static S3LockActions s3LockActions; public static void main(String[] args) { // Get the current date and time to ensure bucket name is unique. diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/DownloadFile.java b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/DownloadFile.java index 99ca37357d8..586cd3af93d 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/DownloadFile.java +++ b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/DownloadFile.java @@ -4,6 +4,7 @@ package com.example.s3.transfermanager; // snippet-start:[s3.tm.java2.downloadfile.import] + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.sync.RequestBody; @@ -23,20 +24,21 @@ // snippet-end:[s3.tm.java2.downloadfile.import] /** - * Before running this Java V2 code example, configure your development - * environment, including your credentials. - * - * For more information, see the following documentation topic: - * - * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + * Before running this example: + *

+ * The SDK must be able to authenticate AWS requests on your behalf. If you have not configured + * authentication for SDKs and tools,see https://docs.aws.amazon.com/sdkref/latest/guide/access.html in the AWS SDKs and Tools Reference Guide. + *

+ * You must have a runtime environment configured with the Java SDK. + * See https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html in the Developer Guide if this is not set up. */ public class DownloadFile { private static final Logger logger = LoggerFactory.getLogger(UploadFile.class); public final String bucketName = "x-" + UUID.randomUUID(); public final String key = UUID.randomUUID().toString(); + private final String downloadedFileName = "downloaded.pdf"; public String downloadedFileWithPath; - private final String downloadedFileName = "downloaded.png"; public DownloadFile() { setUp(); @@ -45,7 +47,7 @@ public DownloadFile() { public static void main(String[] args) { DownloadFile download = new DownloadFile(); download.downloadFile(S3ClientFactory.transferManager, download.bucketName, download.key, - download.downloadedFileWithPath); + download.downloadedFileWithPath); download.cleanUp(); } @@ -53,10 +55,9 @@ public static void main(String[] args) { public Long downloadFile(S3TransferManager transferManager, String bucketName, String key, String downloadedFileWithPath) { DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder() - .getObjectRequest(b -> b.bucket(bucketName).key(key)) - .addTransferListener(LoggingTransferListener.create()) - .destination(Paths.get(downloadedFileWithPath)) - .build(); + .getObjectRequest(b -> b.bucket(bucketName).key(key)) + .destination(Paths.get(downloadedFileWithPath)) + .build(); FileDownload downloadFile = transferManager.downloadFile(downloadFileRequest); @@ -66,13 +67,56 @@ public Long downloadFile(S3TransferManager transferManager, String bucketName, } // snippet-end:[s3.tm.java2.downloadfile.main] + // snippet-start:[s3.tm.java2.trackdownloadfile.main] + public void trackDownloadFile(S3TransferManager transferManager, String bucketName, + String key, String downloadedFileWithPath) { + DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder() + .getObjectRequest(b -> b.bucket(bucketName).key(key)) + .addTransferListener(LoggingTransferListener.create()) // Add listener. + .destination(Paths.get(downloadedFileWithPath)) + .build(); + + FileDownload downloadFile = transferManager.downloadFile(downloadFileRequest); + + CompletedFileDownload downloadResult = downloadFile.completionFuture().join(); + /* + The SDK provides a LoggingTransferListener implementation of the TransferListener interface. + You can also implement the interface to provide your own logic. + + Configure log4J2 with settings such as the following. + + + + + + + + + + + + + + + Log4J2 logs the progress. The following is example output for a 21.3 MB file download. + Transfer initiated... + |======= | 39.4% + |=============== | 78.8% + |====================| 100.0% + Transfer complete! + */ + } + // snippet-end:[s3.tm.java2.trackdownloadfile.main] + + private void setUp() { S3ClientFactory.s3Client.createBucket(b -> b.bucket(bucketName)); - S3ClientFactory.s3Client.putObject(builder -> builder - .bucket(bucketName) - .key(key), RequestBody.fromString("Hello World")); - URL resource = DownloadFile.class.getClassLoader().getResource("."); try { + S3ClientFactory.s3Client.putObject(builder -> builder + .bucket(bucketName) + .key(key), RequestBody.fromFile(Paths.get( + DownloadFile.class.getClassLoader().getResource("multipartUploadFiles/s3-userguide.pdf").toURI()))); + URL resource = DownloadFile.class.getClassLoader().getResource("."); Path basePath = Paths.get(resource.toURI()); Path fullPath = basePath.resolve(downloadedFileName); downloadedFileWithPath = fullPath.toString(); diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3ClientFactory.java b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3ClientFactory.java index 3d69835f284..c0a69f666ab 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3ClientFactory.java +++ b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/S3ClientFactory.java @@ -21,10 +21,10 @@ */ public class S3ClientFactory { - public static final S3TransferManager transferManager = createCustonTm(); + public static final S3TransferManager transferManager = createCustomTm(); public static final S3Client s3Client; - private static S3TransferManager createCustonTm() { + private static S3TransferManager createCustomTm() { // snippet-start:[s3.tm.java2.s3clientfactory.create_custom_tm] S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder() .credentialsProvider(DefaultCredentialsProvider.create()) diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/UploadFile.java b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/UploadFile.java index 55e4fbd8cd1..f446f680325 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/UploadFile.java +++ b/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager/UploadFile.java @@ -19,12 +19,13 @@ // snippet-end:[s3.tm.java2.uploadfile.import] /** - * Before running this Java V2 code example, set up your development - * environment, including your credentials. - * - * For more information, see the following documentation topic: - * - * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + * Before running this example: + *

+ * The SDK must be able to authenticate AWS requests on your behalf. If you have not configured + * authentication for SDKs and tools,see https://docs.aws.amazon.com/sdkref/latest/guide/access.html in the AWS SDKs and Tools Reference Guide. + *

+ * You must have a runtime environment configured with the Java SDK. + * See https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html in the Developer Guide if this is not set up. */ public class UploadFile { @@ -40,6 +41,7 @@ public UploadFile() { public static void main(String[] args) { UploadFile upload = new UploadFile(); upload.uploadFile(S3ClientFactory.transferManager, upload.bucketName, upload.key, upload.filePathURI); + upload.trackUploadFile(S3ClientFactory.transferManager, upload.bucketName, upload.key, upload.filePathURI); upload.cleanUp(); } @@ -48,7 +50,6 @@ public String uploadFile(S3TransferManager transferManager, String bucketName, String key, URI filePathURI) { UploadFileRequest uploadFileRequest = UploadFileRequest.builder() .putObjectRequest(b -> b.bucket(bucketName).key(key)) - .addTransferListener(LoggingTransferListener.create()) .source(Paths.get(filePathURI)) .build(); @@ -59,10 +60,52 @@ public String uploadFile(S3TransferManager transferManager, String bucketName, } // snippet-end:[s3.tm.java2.uploadfile.main] + // snippet-start:[s3.tm.java2.trackuploadfile.main] + public void trackUploadFile(S3TransferManager transferManager, String bucketName, + String key, URI filePathURI) { + UploadFileRequest uploadFileRequest = UploadFileRequest.builder() + .putObjectRequest(b -> b.bucket(bucketName).key(key)) + .addTransferListener(LoggingTransferListener.create()) // Add listener. + .source(Paths.get(filePathURI)) + .build(); + + FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest); + + fileUpload.completionFuture().join(); + /* + The SDK provides a LoggingTransferListener implementation of the TransferListener interface. + You can also implement the interface to provide your own logic. + + Configure log4J2 with settings such as the following. + + + + + + + + + + + + + + + Log4J2 logs the progress. The following is example output for a 21.3 MB file upload. + Transfer initiated... + | | 0.0% + |==== | 21.1% + |============ | 60.5% + |====================| 100.0% + Transfer complete! + */ + } + // snippet-end:[s3.tm.java2.trackuploadfile.main] + private void setUp() { S3ClientFactory.s3Client.createBucket(b -> b.bucket(bucketName)); // get the file system path to the provided file to upload - URL resource = UploadFile.class.getClassLoader().getResource("image.png"); + URL resource = UploadFile.class.getClassLoader().getResource("multipartUploadFiles/s3-userguide.pdf"); try { filePathURI = resource.toURI(); } catch (URISyntaxException | NullPointerException e) { diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/util/MemoryLog4jAppender.java b/javav2/example_code/s3/src/main/java/com/example/s3/util/MemoryLog4jAppender.java index f2d2b7d3cb4..63fc69097bd 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/util/MemoryLog4jAppender.java +++ b/javav2/example_code/s3/src/main/java/com/example/s3/util/MemoryLog4jAppender.java @@ -23,6 +23,7 @@ public class MemoryLog4jAppender extends AbstractAppender { private Map eventMap = new LinkedHashMap<>(); + private StringBuilder stringBuilder = new StringBuilder(); protected MemoryLog4jAppender(String name, Filter filter) { super(name, filter, null); @@ -43,9 +44,13 @@ public void append(LogEvent event) { } else { eventMap.put (eventWithParameters.toString(), null); } + stringBuilder.append(eventWithParameters.getFormattedMessage() + "\n"); } public Map getEventMap(){ return this.eventMap; } + public String getEventsAsString(){ + return stringBuilder.toString(); + } } diff --git a/javav2/example_code/s3/src/main/resources/log4j2.xml b/javav2/example_code/s3/src/main/resources/log4j2.xml index 9f1605fe6b0..4c0eb20893a 100644 --- a/javav2/example_code/s3/src/main/resources/log4j2.xml +++ b/javav2/example_code/s3/src/main/resources/log4j2.xml @@ -1,21 +1,30 @@ - + + + + - - - - + + + - - + + + + + + + + + \ No newline at end of file diff --git a/javav2/example_code/s3/src/test/java/TransferManagerTest.java b/javav2/example_code/s3/src/test/java/TransferManagerTest.java index 05358a7ae68..d00b519815a 100644 --- a/javav2/example_code/s3/src/test/java/TransferManagerTest.java +++ b/javav2/example_code/s3/src/test/java/TransferManagerTest.java @@ -1,5 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 + import com.example.s3.transfermanager.DownloadFile; import com.example.s3.transfermanager.DownloadToDirectory; import com.example.s3.transfermanager.ObjectCopy; @@ -8,6 +9,10 @@ import com.example.s3.transfermanager.UploadFile; import com.example.s3.transfermanager.UploadStream; import com.example.s3.util.AsyncExampleUtils; +import com.example.s3.util.MemoryLog4jAppender; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -18,6 +23,7 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; +import org.opentest4j.AssertionFailedError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.exception.SdkException; @@ -32,11 +38,20 @@ class TransferManagerTest { private static final Logger logger = LoggerFactory.getLogger(TransferManagerTest.class); + private static final String LOGGED_STRING_TO_CHECK = "Transfer initiated..."; + + @BeforeAll + public static void beforeAll() { + logger.info("S3TransferManager tests starting ..."); + } + + @AfterAll + public static void afterAll() { + logger.info("... S3TransferManager tests finished"); + } - @Test - @Order(1) @Tag("IntegrationTest") - public void uploadSingleFileWorks(){ + public void uploadSingleFileWorks() { UploadFile upload = new UploadFile(); String etag = upload.uploadFile(S3ClientFactory.transferManager, upload.bucketName, upload.key, upload.filePathURI); Assertions.assertNotNull(etag); @@ -44,9 +59,22 @@ public void uploadSingleFileWorks(){ } @Test - @Order(2) @Tag("IntegrationTest") - public void downloadSingleFileWorks(){ + public void trackUploadFileWorks() { + UploadFile upload = new UploadFile(); + try { + upload.trackUploadFile(S3ClientFactory.transferManager, upload.bucketName, upload.key, upload.filePathURI); + } catch (SdkException | AssertionFailedError e) { + logger.error(e.getMessage()); + } finally { + upload.cleanUp(); + } + Assertions.assertTrue(getLoggedMessages().contains(LOGGED_STRING_TO_CHECK)); + } + + @Test + @Tag("IntegrationTest") + public void downloadSingleFileWorks() { DownloadFile download = new DownloadFile(); Long fileLength = download.downloadFile(S3ClientFactory.transferManager, download.bucketName, download.key, download.downloadedFileWithPath); Assertions.assertNotEquals(0L, fileLength); @@ -54,9 +82,23 @@ public void downloadSingleFileWorks(){ } @Test - @Order(3) @Tag("IntegrationTest") - public void copyObjectWorks(){ + public void trackDownloadFileWorks() { + DownloadFile download = new DownloadFile(); + try { + download.trackDownloadFile(S3ClientFactory.transferManager, download.bucketName, download.key, download.downloadedFileWithPath); + } catch (SdkException e){ + logger.error(e.getMessage()); + } finally { + download.cleanUp(); + } + Assertions.assertTrue(getLoggedMessages().contains(LOGGED_STRING_TO_CHECK)); + } + + + @Test + @Tag("IntegrationTest") + public void copyObjectWorks() { ObjectCopy copy = new ObjectCopy(); String etag = copy.copyObject(S3ClientFactory.transferManager, copy.bucketName, copy.key, copy.destinationBucket, copy.destinationKey); Assertions.assertNotNull(etag); @@ -64,9 +106,8 @@ public void copyObjectWorks(){ } @Test - @Order(4) @Tag("IntegrationTest") - public void directoryUploadWorks(){ + public void directoryUploadWorks() { UploadADirectory upload = new UploadADirectory(); Integer numFailedUploads = upload.uploadDirectory(S3ClientFactory.transferManager, upload.sourceDirectory, upload.bucketName); Assertions.assertNotNull(numFailedUploads, "Bucket download failed to complete."); @@ -74,9 +115,8 @@ public void directoryUploadWorks(){ } @Test - @Order(5) @Tag("IntegrationTest") - public void directoryDownloadWorks(){ + public void directoryDownloadWorks() { DownloadToDirectory download = new DownloadToDirectory(); Integer numFilesFailedToDownload = download.downloadObjectsToDirectory(S3ClientFactory.transferManager, download.destinationPathURI, download.bucketName); Assertions.assertNotNull(numFilesFailedToDownload, "Bucket download failed to complete."); @@ -84,9 +124,8 @@ public void directoryDownloadWorks(){ } @Test - @Order(6) @Tag("IntegrationTest") - public void uploadStreamWorks(){ + public void uploadStreamWorks() { String bucketName = "x-" + UUID.randomUUID(); String key = UUID.randomUUID().toString(); AsyncExampleUtils.createBucket(bucketName); @@ -104,13 +143,10 @@ public void uploadStreamWorks(){ } } - @BeforeAll - public static void beforeAll(){ - logger.info("S3TransferManager tests starting ..."); - } - - @AfterAll - public static void afterAll(){ - logger.info("... S3TransferManager tests finished"); + private String getLoggedMessages() { + final LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false); + final Configuration configuration = context.getConfiguration(); + final MemoryLog4jAppender memoryLog4jAppender = configuration.getAppender("MemoryLog4jAppender"); + return memoryLog4jAppender.getEventsAsString(); } } \ No newline at end of file diff --git a/javav2/example_code/s3/src/test/java/com/example/s3/ParseUriTest.java b/javav2/example_code/s3/src/test/java/com/example/s3/ParseUriTest.java new file mode 100644 index 00000000000..d968d21a1ab --- /dev/null +++ b/javav2/example_code/s3/src/test/java/com/example/s3/ParseUriTest.java @@ -0,0 +1,43 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.s3; + +import com.example.s3.util.MemoryLog4jAppender; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.services.s3.S3Client; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class ParseUriTest { + + private final S3Client s3 = S3Client.create(); + + @Test + @Order(24) + public void s3UriParsingTest(){ + String url = "https://s3.us-west-1.amazonaws.com/myBucket/resources/doc.txt?versionId=abc123&partNumber=77&partNumber=88"; + ParseUri.parseS3UriExample(s3,url); + final LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false); + final Configuration configuration = context.getConfiguration(); + final MemoryLog4jAppender memoryLog4jAppender = (MemoryLog4jAppender) configuration.getAppender("MemoryLog4jAppender"); + final Map eventMap = memoryLog4jAppender.getEventMap(); + + Assertions.assertTrue(() -> eventMap.get("region").equals("us-west-1")); + Assertions.assertTrue(() -> eventMap.get("bucket").equals("myBucket")); + Assertions.assertTrue(() -> eventMap.get("key").equals("resources/doc.txt")); + Assertions.assertTrue(() -> eventMap.get("isPathStyle").equals("true")); + Assertions.assertTrue(() -> eventMap.get("rawQueryParameters").equals("{versionId=[abc123], partNumber=[77, 88]}")); + Assertions.assertTrue(() -> eventMap.get("firstMatchingRawQueryParameter-versionId").equals("abc123")); + Assertions.assertTrue(() -> eventMap.get("firstMatchingRawQueryParameter-partNumber").equals("77")); + Assertions.assertTrue(() -> eventMap.get("firstMatchingRawQueryParameter").equals("[77, 88]")); + } +} +