diff --git a/.doc_gen/metadata/bedrock-runtime_metadata.yaml b/.doc_gen/metadata/bedrock-runtime_metadata.yaml index 6e6f48b2ffd..5eeddc97e7f 100644 --- a/.doc_gen/metadata/bedrock-runtime_metadata.yaml +++ b/.doc_gen/metadata/bedrock-runtime_metadata.yaml @@ -1219,7 +1219,7 @@ bedrock-runtime_Scenario_ToolUseDemo_AnthropicClaude: - description: "The weather tool used by the demo. This script defines the tool specification and implements the logic to retrieve weather data using from the Open-Meteo API." snippet_tags: - rust.bedrock-runtime.Converse_AnthropicClaude.tool-use.weather-tool - - description: "Utilities to print the Message Content Blocks" + - description: "Utilities to print the Message Content Blocks." snippet_tags: - rust.bedrock-runtime.Converse_AnthropicClaude.tool-use.user-interface - description: "Use statements, Error utility, and constants." diff --git a/.doc_gen/metadata/cross_metadata.yaml b/.doc_gen/metadata/cross_metadata.yaml index f350838f387..db05286b430 100644 --- a/.doc_gen/metadata/cross_metadata.yaml +++ b/.doc_gen/metadata/cross_metadata.yaml @@ -826,7 +826,6 @@ cross_CognitoAutoConfirmUser: versions: - sdk_version: 3 github: javascriptv3/example_code/cross-services/wkflw-pools-triggers - sdk_guide: excerpts: - description: | Configure an interactive "Scenario" run. The JavaScript (v3) examples diff --git a/.doc_gen/metadata/dynamodb_metadata.yaml b/.doc_gen/metadata/dynamodb_metadata.yaml index 071bf1c0a60..487de508b08 100644 --- a/.doc_gen/metadata/dynamodb_metadata.yaml +++ b/.doc_gen/metadata/dynamodb_metadata.yaml @@ -205,10 +205,10 @@ dynamodb_BatchGetItem: - sdk_version: 2 github: javav2/example_code/dynamodb excerpts: - - description: shows how to get batch items using the service client. + - description: Shows how to get batch items using the service client. snippet_tags: - dynamodb.java2.batch.get.items.main - - description: shows how to get batch items using the service client and a paginator. + - description: Shows how to get batch items using the service client and a paginator. snippet_tags: - dynamodb.java2.batch.get.items.paginator.main .NET: diff --git a/.doc_gen/metadata/iot_sitewise_metadata.yaml b/.doc_gen/metadata/iot_sitewise_metadata.yaml index 3aa344b21a1..7ad95ebc540 100644 --- a/.doc_gen/metadata/iot_sitewise_metadata.yaml +++ b/.doc_gen/metadata/iot_sitewise_metadata.yaml @@ -185,7 +185,7 @@ iotsitewise_CreateAssetModel: services: iotsitewise: {CreateAssetModel} iotsitewise_Scenario: - synopsis: Learn core operations for &ITSWlong; using an &AWS; SDK. + synopsis: learn core operations for &ITSWlong; using an &AWS; SDK. category: Basics languages: Java: diff --git a/.doc_gen/metadata/medical-imaging_metadata.yaml b/.doc_gen/metadata/medical-imaging_metadata.yaml index 9ae05d50516..aa1bad94234 100644 --- a/.doc_gen/metadata/medical-imaging_metadata.yaml +++ b/.doc_gen/metadata/medical-imaging_metadata.yaml @@ -1232,28 +1232,28 @@ medical-imaging_Scenario_ImageSetsAndFrames: github: javascriptv3/example_code/medical-imaging github_note_at_bottom: true excerpts: - - description: index.js - Orchestrate steps. + - description: Orchestrate steps (index.js). snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/index.js - - description: deploy-steps.js - Deploy resources. + - description: Deploy resources (deploy-steps.js). snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/deploy-steps.js - - description: dataset-steps.js - Copy DICOM files. + - description: Copy DICOM files (dataset-steps.js). snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/dataset-steps.js - - description: import-steps.js - Start import into datastore. + - description: Start import into datastore (import-steps.js). snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/import-steps.js - - description: image-set-steps.js - Get image set IDs. + - description: Get image set IDs (image-set-steps.js - ). snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/image-set-steps.js - - description: image-frame-steps.js - Get image frame IDs. + - description: Get image frame IDs (image-frame-steps.js). snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/image-frame-steps.js - - description: verify-steps.js - Verify image frames. The &AWS; HealthImaging Pixel Data Verification library was used for verification. + - description: Verify image frames (verify-steps.js). The &AWS; HealthImaging Pixel Data Verification library was used for verification. snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/verify-steps.js - - description: clean-up-steps.js - Destroy resources. + - description: Destroy resources (clean-up-steps.js). snippet_files: - javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/clean-up-steps.js services: diff --git a/.doc_gen/metadata/redshift_metadata.yaml b/.doc_gen/metadata/redshift_metadata.yaml index 7f77e2b3f2b..2f4392bf8b7 100644 --- a/.doc_gen/metadata/redshift_metadata.yaml +++ b/.doc_gen/metadata/redshift_metadata.yaml @@ -9,9 +9,9 @@ redshift_Hello: versions: - sdk_version: 2 github: gov2/redshift - genai: some excerpts: - description: + genai: some snippet_tags: - gov2.redshift.Hello Java: diff --git a/.doc_gen/metadata/s3-control_metadata.yaml b/.doc_gen/metadata/s3-control_metadata.yaml index dc082a299df..589285de221 100644 --- a/.doc_gen/metadata/s3-control_metadata.yaml +++ b/.doc_gen/metadata/s3-control_metadata.yaml @@ -1,7 +1,7 @@ s3-control_Hello: title: Hello &S3Control; - title_abbrev: Hello '&S3Control;' - synopsis: get started using '&S3Control;' + title_abbrev: Hello &S3Control; + synopsis: get started using &S3Control;. category: Hello languages: Java: @@ -117,7 +117,7 @@ s3-control_UpdateJobPriority: services: s3-control: {UpdateJobPriority} s3-control_Basics: - synopsis: learn core operations for'&S3Control;'. + synopsis: learn core operations for &S3Control;. category: Basics languages: Java: diff --git a/.doc_gen/metadata/s3-directory-buckets_metadata.yaml b/.doc_gen/metadata/s3-directory-buckets_metadata.yaml index 4c0e813c27f..9d2eca0fe04 100644 --- a/.doc_gen/metadata/s3-directory-buckets_metadata.yaml +++ b/.doc_gen/metadata/s3-directory-buckets_metadata.yaml @@ -393,14 +393,14 @@ s3-directory-buckets_UploadPart: s3-directory-buckets_Scenario_ExpressBasics: synopsis_list: - - Set up a VPC and VPC Endpoint - - Set up the Policies, Roles, and User to work with S3 directory buckets and the S3 Express One Zone storage class - - Create two S3 Clients - - Create two buckets - - Create an object and copy it over - - Demonstrate performance difference - - Populate the buckets to show the lexicographical difference - - Prompt the user to see if they want to clean up the resources + - Set up a VPC and VPC Endpoint. + - Set up the Policies, Roles, and User to work with S3 directory buckets and the S3 Express One Zone storage class. + - Create two S3 Clients. + - Create two buckets. + - Create an object and copy it over. + - Demonstrate performance difference. + - Populate the buckets to show the lexicographical difference. + - Prompt the user to see if they want to clean up the resources. category: Basics languages: PHP: diff --git a/.doc_gen/metadata/s3_metadata.yaml b/.doc_gen/metadata/s3_metadata.yaml index 18f6be48f7b..60871ff15e0 100644 --- a/.doc_gen/metadata/s3_metadata.yaml +++ b/.doc_gen/metadata/s3_metadata.yaml @@ -74,7 +74,7 @@ s3_Hello: excerpts: - snippet_tags: - s3.rust.s3-helloworld - - description: S3ExampleError utilities + - description: S3ExampleError utilities. - snippet_tags: - s3.rust.s3-example-error services: @@ -2722,7 +2722,7 @@ s3_Scenario_PresignedUrl: snippet_tags: - presigned.java2.generatepresignedgeturlandretrieve.sdkhttpclient - - description: Generate a pre-signed URL for an upload, then upload a file (PUT request). + - description: Generate a pre-signed URL with metadata in headers for an upload, then upload a file (PUT request). - description: Imports. snippet_tags: - presigned.java2.generatepresignedurlandputfilewithmetadata.import @@ -2739,6 +2739,17 @@ s3_Scenario_PresignedUrl: - description: Use the &AWS; for Java V2 SdkHttpClient class to do the upload. snippet_tags: - presigned.java2.generatepresignedurlandputfilewithmetadata.sdkhttpclient + + - description: Generate a pre-signed URL with query parameters for an upload, then upload a file (PUT request). + - description: Imports. + snippet_tags: + - presigned.java2.generatepresignedurlandputfilewithqueryparams.import + - description: Generate the URL. + snippet_tags: + - presigned.java2.generatepresignedurlandputfilewithqueryparams.createpresignedurl + - description: Use the &AWS; for Java V2 SdkHttpClient class to do the upload. + snippet_tags: + - presigned.java2.generatepresignedurlandputfilewithqueryparams.sdkhttpclient Python: versions: - sdk_version: 3 @@ -3343,20 +3354,20 @@ s3_Scenario_ObjectLock: sdkguide: excerpts: - description: | - index.js - Entrypoint for the workflow. This orchestrates all of the steps. + Entrypoint for the workflow (index.js). This orchestrates all of the steps. Visit GitHub to see the implementation details for Scenario, ScenarioInput, ScenarioOutput, and ScenarioAction. snippet_files: - javascriptv3/example_code/s3/scenarios/object-locking/index.js - - description: welcome.steps.js - Output welcome messages to the console. + - description: Output welcome messages to the console (welcome.steps.js). snippet_files: - javascriptv3/example_code/s3/scenarios/object-locking/welcome.steps.js - - description: setup.steps.js - Deploy buckets, objects, and file settings. + - description: Deploy buckets, objects, and file settings (setup.steps.js). snippet_files: - javascriptv3/example_code/s3/scenarios/object-locking/setup.steps.js - - description: repl.steps.js - View and delete files in the buckets. + - description: View and delete files in the buckets (repl.steps.js). snippet_files: - javascriptv3/example_code/s3/scenarios/object-locking/repl.steps.js - - description: clean.steps.js - Destroy all created resources. + - description: Destroy all created resources (clean.steps.js). snippet_files: - javascriptv3/example_code/s3/scenarios/object-locking/clean.steps.js services: @@ -3447,7 +3458,7 @@ s3_Scenario_AbortMultipartUpload: services: s3: {ListMultipartUploads, AbortMultipartUpload, PutBucketLifecycleConfiguration} s3_Scenario_ProcessS3EventNotification: - title: Receive and process &S3; event notifications by using an &AWS; SDK. + title: Receive and process &S3; event notifications by using an &AWS; SDK title_abbrev: Process S3 event notifications synopsis: work with S3 event notifications in an object-oriented way. category: Scenarios @@ -3465,7 +3476,7 @@ s3_Scenario_ProcessS3EventNotification: s3: {PutBucketNotificationConfiguration} sqs: {ReceiveMessage, GetQueueAttributes, DeleteMessageBatch} s3_Scenario_DeleteAllObjects: - title: Delete all objects in a given &S3; bucket using an &AWS; SDK. + title: Delete all objects in a given &S3; bucket using an &AWS; SDK title_abbrev: Delete all objects in a bucket synopsis: delete all of the objects in an &S3; bucket. category: Scenarios @@ -3482,7 +3493,7 @@ s3_Scenario_DeleteAllObjects: services: s3: {DeleteObjects, ListObjectsV2} s3_Scenario_ConditionalRequests: - title: Make &S3; conditional requests using an &AWS; SDK. + title: Make &S3; conditional requests using an &AWS; SDK title_abbrev: Make conditional requests synopsis: add preconditions to &S3; requests. category: Scenarios diff --git a/.doc_gen/metadata/ssm_metadata.yaml b/.doc_gen/metadata/ssm_metadata.yaml index 978e050a350..b3cc04d35b1 100644 --- a/.doc_gen/metadata/ssm_metadata.yaml +++ b/.doc_gen/metadata/ssm_metadata.yaml @@ -26,9 +26,9 @@ ssm_Hello: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.Hello services: @@ -110,9 +110,9 @@ ssm_CreateOpsItem: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.OpsItemWrapper.decl - python.example_code.ssm.CreateOpsItem @@ -142,9 +142,9 @@ ssm_UpdateMaintenanceWindow: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.MaintenanceWindowWrapper.decl - python.example_code.ssm.UpdateMaintenanceWindow @@ -174,9 +174,9 @@ ssm_CreateMaintenanceWindow: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.MaintenanceWindowWrapper.decl - python.example_code.ssm.CreateMaintenanceWindow @@ -206,9 +206,9 @@ ssm_SendCommand: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.DocumentWrapper.decl - python.example_code.ssm.SendCommand @@ -229,9 +229,9 @@ ssm_ListCommandInvocations: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.DocumentWrapper.decl - python.example_code.ssm.ListCommandInvocations @@ -261,9 +261,9 @@ ssm_CreateDocument: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.DocumentWrapper.decl - python.example_code.ssm.CreateDocument @@ -293,9 +293,9 @@ ssm_DescribeOpsItems: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.OpsItemWrapper.decl - python.example_code.ssm.DescribeOpsItem @@ -307,9 +307,9 @@ ssm_DeleteOpsItem: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.OpsItemWrapper.decl - python.example_code.ssm.DeleteOpsItem @@ -339,9 +339,9 @@ ssm_DeleteMaintenanceWindow: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.MaintenanceWindowWrapper.decl - python.example_code.ssm.DeleteMaintenanceWindow @@ -371,9 +371,9 @@ ssm_DeleteDocument: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.DocumentWrapper.decl - python.example_code.ssm.DeleteDocument @@ -403,9 +403,9 @@ ssm_UpdateOpsItem: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: + genai: some snippet_tags: - python.example_code.ssm.OpsItemWrapper.decl - python.example_code.ssm.UpdateOpsItem @@ -432,27 +432,30 @@ ssm_Scenario: - sdk_version: 3 github: javascriptv3/example_code/ssm sdkguide: - genai: some excerpts: - description: + genai: some snippet_tags: - ssm.JavaScript.Basics.scenario Python: versions: - sdk_version: 3 github: python/example_code/ssm - genai: some excerpts: - description: Run an interactive scenario at a command prompt. + genai: some snippet_tags: - python.example_code.ssm.Scenario_GetStartedSSM - description: Define a class that wraps document and command actions. + genai: some snippet_tags: - python.example_code.ssm.DocumentWrapper.class - description: Define a class that wraps ops item actions. + genai: some snippet_tags: - python.example_code.ssm.OpsItemWrapper.class - description: Define a class that wraps maintenance window actions. + genai: some snippet_tags: - python.example_code.ssm.MaintenanceWindowWrapper.class services: diff --git a/.github/workflows/validate-doc-metadata.yml b/.github/workflows/validate-doc-metadata.yml index 47f6c977f2b..1405ea564d1 100644 --- a/.github/workflows/validate-doc-metadata.yml +++ b/.github/workflows/validate-doc-metadata.yml @@ -16,7 +16,7 @@ jobs: - name: checkout repo content uses: actions/checkout@v4 - name: validate metadata - uses: awsdocs/aws-doc-sdk-examples-tools@2024.47.3 + uses: awsdocs/aws-doc-sdk-examples-tools@2024.49.0 with: doc_gen_only: "False" strict_titles: "True" diff --git a/javascriptv3/example_code/cross-services/wkflw-pools-triggers/tests/scenario-auto-confirm.integration.test.js b/javascriptv3/example_code/cross-services/wkflw-pools-triggers/tests/scenario-auto-confirm.integration.test.js index d509da4e181..ef5e1b1cd23 100644 --- a/javascriptv3/example_code/cross-services/wkflw-pools-triggers/tests/scenario-auto-confirm.integration.test.js +++ b/javascriptv3/example_code/cross-services/wkflw-pools-triggers/tests/scenario-auto-confirm.integration.test.js @@ -34,15 +34,18 @@ describe("Scenario - AutoConfirm", () => { Capabilities: [Capability.CAPABILITY_NAMED_IAM], }), ); - await retry({ intervalInMs: 2000, maxRetries: 60 }, async () => { - const { Stacks } = await cloudformationClient.send( - new DescribeStacksCommand({ StackName: stackName }), - ); - const stack = Stacks[0]; - if (stack.StackStatus !== "CREATE_COMPLETE") { - throw new Error("Stack creation incomplete."); - } - }); + await retry( + { intervalInMs: 2000, maxRetries: 60, backoff: 1000 }, + async () => { + const { Stacks } = await cloudformationClient.send( + new DescribeStacksCommand({ StackName: stackName }), + ); + const stack = Stacks[0]; + if (stack.StackStatus !== "CREATE_COMPLETE") { + throw new Error("Stack creation incomplete."); + } + }, + ); }); afterAll(async () => { diff --git a/javascriptv3/example_code/libs/utils/util-timers.js b/javascriptv3/example_code/libs/utils/util-timers.js index 51d2245952b..e43f1551748 100644 --- a/javascriptv3/example_code/libs/utils/util-timers.js +++ b/javascriptv3/example_code/libs/utils/util-timers.js @@ -10,13 +10,13 @@ const wait = (seconds) => /** * @template T - * @param {{ intervalInMs: number, maxRetries: number, swallowError?: boolean, quiet?: boolean }} config + * @param {{ intervalInMs: number, maxRetries: number, backoff?: number, swallowError?: boolean, quiet?: boolean }} config * @param {() => Promise} fn * @returns {Promise} */ const retry = (config, fn) => new Promise((resolve, reject) => { - const { intervalInMs = 500, maxRetries = 10 } = config; + const { intervalInMs = 500, maxRetries = 10, backoff = 0 } = config; fn() .then(resolve) .catch((err) => { @@ -32,10 +32,14 @@ const retry = (config, fn) => config.swallowError ? resolve() : reject(err); } else { setTimeout(() => { - retry({ ...config, maxRetries: maxRetries - 1 }, fn).then( - resolve, - reject, - ); + retry( + { + ...config, + intervalInMs: intervalInMs + backoff, + maxRetries: maxRetries - 1, + }, + fn, + ).then(resolve, reject); }, intervalInMs); } }); diff --git a/javav2/example_code/iotsitewise/README.md b/javav2/example_code/iotsitewise/README.md index 71c24b6f9b5..e970fefafb7 100644 --- a/javav2/example_code/iotsitewise/README.md +++ b/javav2/example_code/iotsitewise/README.md @@ -79,7 +79,7 @@ This example shows you how to get started using AWS IoT SiteWise. #### Learn the basics -This example shows you how to Learn core operations for AWS IoT SiteWise using an AWS SDK. +This example shows you how to learn core operations for AWS IoT SiteWise using an AWS SDK. @@ -116,4 +116,4 @@ in the `javav2` folder. Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +SPDX-License-Identifier: Apache-2.0 diff --git a/javav2/example_code/s3/README.md b/javav2/example_code/s3/README.md index ac0770d46ad..2b4ef31b31b 100644 --- a/javav2/example_code/s3/README.md +++ b/javav2/example_code/s3/README.md @@ -98,7 +98,8 @@ functions within the same service. - [Create a presigned URL for download](src/main/java/com/example/s3/GeneratePresignedGetUrlAndRetrieve.java) -- [Create a presigned URL for upload](src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java) +- [Create a presigned URL with metadata in headers for upload](src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java) +- [Create a presigned URL with query parameters for upload](src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java) ## Run the examples diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java b/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java new file mode 100644 index 00000000000..c56243006a0 --- /dev/null +++ b/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithQueryParams.java @@ -0,0 +1,158 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.example.s3; + +// snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.import] +import com.example.s3.util.PresignUrlUtils; +import org.slf4j.Logger; +import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; +import software.amazon.awssdk.core.internal.sync.FileContentStreamProvider; +import software.amazon.awssdk.http.HttpExecuteRequest; +import software.amazon.awssdk.http.HttpExecuteResponse; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.Map; +import java.util.UUID; +// snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.import] + +/** + * Before running this Java V2 code example, set up your development environment. + *

+ * For more information, see the following documentation topic: + *

+ * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + */ + +public class GeneratePresignedUrlAndPutFileWithQueryParams { + private static final Logger logger = org.slf4j.LoggerFactory.getLogger(GeneratePresignedUrlAndPutFileWithQueryParams.class); + private final static S3Client s3Client = S3Client.create(); + + public static void main(String[] args) { + String bucketName = "amzn-s3-demo-bucket" + UUID.randomUUID(); // Change bucket name. + String keyName = "key" + UUID.randomUUID(); + String resourcePath = "uploadDirectory/file1.txt"; + // Uncomment the following two lines and comment out the previous two lines to use an image file instead of a PDF file. + //String resourcePath = "image.png"; + //String contentType = "image/png"; + + Map queryParams = Map.of( + "x-amz-meta-author", "Bob", + "x-amz-meta-version", "1.0.0.0", + "x-amz-acl", "private", + "x-amz-server-side-encryption", "AES256" + ); + + PresignUrlUtils.createBucket(bucketName, s3Client); + GeneratePresignedUrlAndPutFileWithQueryParams presign = new GeneratePresignedUrlAndPutFileWithQueryParams(); + try { + String presignedUrlString = presign.createPresignedUrl(bucketName, keyName, queryParams); + presign.useSdkHttpClientToPut(presignedUrlString, getFileForForClasspathResource(resourcePath)); + + } finally { + PresignUrlUtils.deleteObject(bucketName, keyName, s3Client); + PresignUrlUtils.deleteBucket(bucketName, s3Client); + } + } + + // snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.main] + // snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.createpresignedurl] + /** + * Creates a presigned URL to use in a subsequent HTTP PUT request. The code adds query parameters + * to the request instead of using headers. By using query parameters, you do not need to add the + * the parameters as headers when the PUT request is eventually sent. + * + * @param bucketName Bucket name where the object will be uploaded. + * @param keyName Key name of the object that will be uploaded. + * @param queryParams Query string parameters to be added to the presigned URL. + * @return + */ + public String createPresignedUrl(String bucketName, String keyName, Map queryParams) { + try (S3Presigner presigner = S3Presigner.create()) { + // Create an override configuration to store the query parameters. + AwsRequestOverrideConfiguration.Builder overrideConfigurationBuilder = AwsRequestOverrideConfiguration.builder(); + + queryParams.forEach(overrideConfigurationBuilder::putRawQueryParameter); + + PutObjectRequest objectRequest = PutObjectRequest.builder() + .bucket(bucketName) + .key(keyName) + .overrideConfiguration(overrideConfigurationBuilder.build()) // Add the override configuration. + .build(); + + PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() + .signatureDuration(Duration.ofMinutes(10)) // The URL expires in 10 minutes. + .putObjectRequest(objectRequest) + .build(); + + + PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest); + String myURL = presignedRequest.url().toString(); + logger.info("Presigned URL to upload a file to: [{}]", myURL); + logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method()); + + return presignedRequest.url().toExternalForm(); + } + } + // snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.createpresignedurl] + + // snippet-start:[presigned.java2.generatepresignedurlandputfilewithqueryparams.sdkhttpclient] + /** + * Use the AWS SDK for Java V2 SdkHttpClient class to execute the PUT request. Since the + * URL contains the query parameters, no headers are needed for metadata, SSE settings, or ACL settings. + * + * @param presignedUrlString The URL for the PUT request. + * @param fileToPut File to uplaod + */ + public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut) { + logger.info("Begin [{}] upload", fileToPut.toString()); + + try { + URL presignedUrl = new URL(presignedUrlString); + + SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder() + .method(SdkHttpMethod.PUT) + .uri(presignedUrl.toURI()); + + SdkHttpRequest request = requestBuilder.build(); + + HttpExecuteRequest executeRequest = HttpExecuteRequest.builder() + .request(request) + .contentStreamProvider(new FileContentStreamProvider(fileToPut.toPath())) + .build(); + + try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) { + HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call(); + logger.info("Response code: {}", response.httpResponse().statusCode()); + } + } catch (URISyntaxException | IOException e) { + logger.error(e.getMessage(), e); + } + } + // snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.sdkhttpclient] + // snippet-end:[presigned.java2.generatepresignedurlandputfilewithqueryparams.main] + + public static File getFileForForClasspathResource(String resourcePath) { + try { + URL resource = GeneratePresignedUrlAndUploadObject.class.getClassLoader().getResource(resourcePath); + return Paths.get(resource.toURI()).toFile(); + } catch (URISyntaxException e) { + logger.error(e.getMessage(), e); + } + return null; + } +} diff --git a/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md b/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md index 0843d40fd05..ea544fd5925 100644 --- a/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md +++ b/javav2/example_code/s3/src/main/java/com/example/s3/batch/README.md @@ -31,7 +31,7 @@ For prerequisites, see the [README](../../../../../../../../../README.md#Prerequ ### Get started -- [Hello 'Amazon S3 Control'](HelloS3Batch.java#L5) (`ListJobs`) +- [Hello Amazon S3 Control](HelloS3Batch.java#L5) (`ListJobs`) ### Basics @@ -65,14 +65,14 @@ Code excerpts that show you how to call individual service functions. -#### Hello 'Amazon S3 Control' +#### Hello Amazon S3 Control -This example shows you how to get started using 'Amazon S3 Control' +This example shows you how to get started using Amazon S3 Control. #### Learn the basics -This example shows you how to learn core operations for'Amazon S3 Control'. +This example shows you how to learn core operations for Amazon S3 Control. @@ -109,4 +109,4 @@ in the `javav2` folder. Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +SPDX-License-Identifier: Apache-2.0 diff --git a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java b/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java index ce331823256..efe6a7690fc 100644 --- a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java +++ b/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java @@ -4,6 +4,7 @@ package com.example.s3.presignurl; import com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata; +import com.example.s3.GeneratePresignedUrlAndPutFileWithQueryParams; import com.example.s3.GeneratePresignedUrlAndUploadObject; import com.example.s3.util.PresignUrlUtils; import org.junit.jupiter.api.AfterAll; @@ -16,6 +17,9 @@ import java.io.File; import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -23,7 +27,9 @@ class GeneratePresignedPutUrlTests { private static final String BUCKET_NAME = "b-" + UUID.randomUUID(); private static final String KEY_NAME = "k-" + UUID.randomUUID(); private static final S3Client s3Client = S3Client.create(); - private static final Map METADATA = Map.of("meta1", "value1"); + private static final String METADATA_KEY = "meta1"; + private static final String METADATA_VALUE = "value1"; + private static final Map METADATA = Map.of(METADATA_KEY, METADATA_VALUE); private static final File PDF_FILE = GeneratePresignedUrlAndPutFileWithMetadata .getFileForForClasspathResource("multipartUploadFiles/s3-userguide.pdf"); @@ -51,13 +57,63 @@ void testCreatePresignedUrlForPutString() { Assertions.assertTrue(objectExists()); } + @Test + @Tag("IntegrationTest") + void test_create_presigned_url_using_query_params_does_not_add_to_signed_headers() { + Map queryParams = Map.of( + "x-amz-meta-author", "Bob", + "x-amz-meta-version", "1.0.0.0", + "x-amz-acl", "private", + "x-amz-server-side-encryption", "AES256" + ); + + GeneratePresignedUrlAndPutFileWithQueryParams presign = new GeneratePresignedUrlAndPutFileWithQueryParams(); + String presignedUrl = presign.createPresignedUrl(BUCKET_NAME, KEY_NAME, queryParams); + + Map queryStringMap = parseQueryString(presignedUrl); + Assertions.assertFalse(queryStringMap.get("X-Amz-SignedHeaders").contains("x-amz-meta-author")); + Assertions.assertFalse(queryStringMap.get("X-Amz-SignedHeaders").contains("x-amz-server-side-encryption")); + } + + @Test + @Tag("IntegrationTest") + void test_create_presigned_url_using_query_params_works() { + Map queryParams = Map.of( + "x-amz-meta-author", "Bob", + "x-amz-meta-version", "1.0.0.0", + "x-amz-server-side-encryption", "AES256" + ); + + GeneratePresignedUrlAndPutFileWithQueryParams classUnderTest = new GeneratePresignedUrlAndPutFileWithQueryParams(); + String presignedUrl = classUnderTest.createPresignedUrl(BUCKET_NAME, KEY_NAME, queryParams); + + classUnderTest.useSdkHttpClientToPut(presignedUrl, PDF_FILE); + + try (S3Client s3Client = S3Client.create()){ + s3Client.getObject(builder -> { + builder.bucket(BUCKET_NAME); + builder.key(KEY_NAME); + builder.build(); + }, (response, stream) -> { + stream.abort(); + Assertions.assertEquals("Bob", response.metadata().get("author")); + Assertions.assertEquals("1.0.0.0", response.metadata().get("version")); + Assertions.assertEquals("AES256", response.serverSideEncryptionAsString()); + return null; + }); + } + } + @Test @Tag("IntegrationTest") void testCreatePresignedUrlForPut() { GeneratePresignedUrlAndPutFileWithMetadata presignInstanceUnderTest = new GeneratePresignedUrlAndPutFileWithMetadata(); final String presignedUrlString = presignInstanceUnderTest.createPresignedUrl(BUCKET_NAME, KEY_NAME, METADATA); - Assertions.assertTrue(presignedUrlString.contains("meta1")); + final Map queryParamsMap = parseQueryString(presignedUrlString); + // Assert that the metadata key, but not the value, is in the signed headers. + Assertions.assertTrue(queryParamsMap.get("X-Amz-SignedHeaders").contains("x-amz-meta-" + METADATA_KEY)); + Assertions.assertFalse(queryParamsMap.get("X-Amz-SignedHeaders").contains("x-amz-meta-" + METADATA_VALUE)); } @Test @@ -106,4 +162,47 @@ private static Boolean objectHasMetadata() { // The SDK strips off the leading "x-amz-meta-" from the metadata key. return response.metadata().get("meta1").equals(GeneratePresignedPutUrlTests.METADATA.get("meta1")); } + + public static Map parseQueryString(String queryString) { + Map params = new HashMap<>(); + + // Return empty map for null or empty input + if (queryString == null || queryString.isEmpty()) { + return params; + } + + // Split the query string into key-value pairs + String[] pairs = queryString.split("&"); + + for (String pair : pairs) { + // Find the separator between key and value + int separatorIndex = pair.indexOf("="); + + // Handle key-only parameters (no value) + if (separatorIndex <= 0) { + params.put(URLDecoder.decode(pair, StandardCharsets.UTF_8), null); + continue; + } + + // Extract and decode the key + String key = URLDecoder.decode( + pair.substring(0, separatorIndex), + StandardCharsets.UTF_8 + ); + + // Extract and decode the value if it exists + String value = null; + if (pair.length() > separatorIndex + 1) { + value = URLDecoder.decode( + pair.substring(separatorIndex + 1), + StandardCharsets.UTF_8 + ); + } + + params.put(key, value); + } + + return params; + } + } \ No newline at end of file diff --git a/php/example_code/s3/express/README.md b/php/example_code/s3/express/README.md index 96231648ecd..e1caa09d375 100644 --- a/php/example_code/s3/express/README.md +++ b/php/example_code/s3/express/README.md @@ -52,14 +52,14 @@ Code examples that show you how to perform the essential operations within a ser This example shows you how to do the following: -- Set up a VPC and VPC Endpoint -- Set up the Policies, Roles, and User to work with S3 directory buckets and the S3 Express One Zone storage class -- Create two S3 Clients -- Create two buckets -- Create an object and copy it over -- Demonstrate performance difference -- Populate the buckets to show the lexicographical difference -- Prompt the user to see if they want to clean up the resources +- Set up a VPC and VPC Endpoint. +- Set up the Policies, Roles, and User to work with S3 directory buckets and the S3 Express One Zone storage class. +- Create two S3 Clients. +- Create two buckets. +- Create an object and copy it over. +- Demonstrate performance difference. +- Populate the buckets to show the lexicographical difference. +- Prompt the user to see if they want to clean up the resources. diff --git a/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift b/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift index 824464398e1..2ed004f135d 100644 --- a/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift +++ b/swift/example_code/s3/presigned-urls/Sources/presigned-upload/entry.swift @@ -171,7 +171,6 @@ struct ExampleCommand: ParsableCommand { } catch { throw TransferError.signingError } - // snippet-end:[swift.s3.presigned.presign-PutObject-advanced] // Send the HTTP request and upload the file to Amazon S3. @@ -180,6 +179,7 @@ struct ExampleCommand: ParsableCommand { request.body = .bytes(fileData) _ = try await HTTPClient.shared.execute(request, timeout: .seconds(5*60)) + // snippet-end:[swift.s3.presigned.presign-PutObject-advanced] print("Uploaded (presigned) \(sourcePath) to \(bucket)/\(fileName).") }